inertia-sails 0.2.2 → 0.3.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/index.js +142 -12
- package/lib/handle-bad-request.js +65 -0
- package/lib/helpers/build-page-object.js +27 -0
- package/lib/helpers/ignore-first-load-symbol.js +1 -0
- package/{private → lib/helpers}/inertia-headers.js +3 -0
- package/lib/helpers/is-inertia-partial-request.js +4 -0
- package/{private → lib/helpers}/resolve-validation-errors.js +4 -1
- package/lib/location.js +17 -0
- package/{private → lib/middleware}/inertia-middleware.js +2 -2
- package/lib/props/always-prop.js +8 -0
- package/lib/props/defer-prop.js +15 -0
- package/lib/props/merge-prop.js +9 -0
- package/lib/props/mergeable-prop.js +10 -0
- package/lib/props/optional-prop.js +8 -0
- package/lib/props/pick-props-to-resolve.js +34 -0
- package/lib/props/resolve-deferred-props.js +15 -0
- package/lib/props/resolve-except-props.js +9 -0
- package/lib/props/resolve-merge-props.js +13 -0
- package/lib/props/resolve-only-props.js +21 -0
- package/lib/props/resolve-page-props.js +24 -0
- package/lib/props/resolve-prop.js +27 -0
- package/lib/render.js +35 -0
- package/package.json +5 -5
- package/inertia-sails-0.2.0.tgz +0 -0
- /package/{private → lib/helpers}/is-inertia-request.js +0 -0
- /package/{private → lib/props}/get-partial-data.js +0 -0
package/index.js
CHANGED
|
@@ -4,7 +4,16 @@
|
|
|
4
4
|
* @description :: A hook definition. Extends Sails by adding shadow routes, implicit actions, and/or initialization logic.
|
|
5
5
|
* @docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
|
|
6
6
|
*/
|
|
7
|
-
const inertia = require('./
|
|
7
|
+
const inertia = require('./lib/middleware/inertia-middleware')
|
|
8
|
+
const render = require('./lib/render')
|
|
9
|
+
const location = require('./lib/location')
|
|
10
|
+
|
|
11
|
+
const DeferProp = require('./lib/props/defer-prop')
|
|
12
|
+
const OptionalProp = require('./lib/props/optional-prop')
|
|
13
|
+
const MergeProp = require('./lib/props/merge-prop')
|
|
14
|
+
const AlwaysProp = require('./lib/props/always-prop')
|
|
15
|
+
const handleBadRequest = require('./lib/handle-bad-request')
|
|
16
|
+
|
|
8
17
|
module.exports = function defineInertiaHook(sails) {
|
|
9
18
|
let hook
|
|
10
19
|
const routesToBindInertiaTo = [
|
|
@@ -20,7 +29,10 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
20
29
|
defaults: {
|
|
21
30
|
inertia: {
|
|
22
31
|
rootView: 'app',
|
|
23
|
-
version: 1
|
|
32
|
+
version: 1,
|
|
33
|
+
history: {
|
|
34
|
+
encrypt: false
|
|
35
|
+
}
|
|
24
36
|
}
|
|
25
37
|
},
|
|
26
38
|
initialize: async function () {
|
|
@@ -28,27 +40,145 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
28
40
|
sails.inertia = hook
|
|
29
41
|
sails.inertia.sharedProps = {}
|
|
30
42
|
sails.inertia.sharedViewData = {}
|
|
31
|
-
sails.
|
|
32
|
-
|
|
43
|
+
sails.inertia.shouldEncryptHistory = sails.config.inertia.history.encrypt
|
|
44
|
+
sails.inertia.shouldClearHistory = false
|
|
45
|
+
sails.on('router:before', function () {
|
|
46
|
+
routesToBindInertiaTo.forEach(function (routeAddress) {
|
|
33
47
|
sails.router.bind(routeAddress, inertia(hook))
|
|
34
48
|
})
|
|
35
49
|
})
|
|
36
50
|
},
|
|
37
51
|
|
|
38
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Share a property globally
|
|
54
|
+
* @param {string} key - The key of the property
|
|
55
|
+
* @param {*} value - The value of the property
|
|
56
|
+
*/
|
|
57
|
+
share(key, value = null) {
|
|
58
|
+
return (sails.inertia.sharedProps[key] = value)
|
|
59
|
+
},
|
|
39
60
|
|
|
40
|
-
|
|
41
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Get shared properties
|
|
63
|
+
* @param {string|null} key - The key of the property to get, or null to get all
|
|
64
|
+
* @returns {*} - The shared property or all shared properties
|
|
65
|
+
*/
|
|
66
|
+
getShared(key = null) {
|
|
67
|
+
return sails.inertia.sharedProps[key] ?? sails.inertia.sharedProps
|
|
68
|
+
},
|
|
42
69
|
|
|
43
|
-
|
|
44
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Flush shared properties
|
|
72
|
+
* @param {string|null} key - The key of the property to flush, or null to flush all
|
|
73
|
+
*/
|
|
74
|
+
flushShared(key) {
|
|
75
|
+
return key
|
|
45
76
|
? delete sails.inertia.sharedProps[key]
|
|
46
77
|
: (sails.inertia.sharedProps = {})
|
|
47
78
|
},
|
|
48
79
|
|
|
49
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Add view data
|
|
82
|
+
* @param {string} key - The key of the view data
|
|
83
|
+
* @param {*} value - The value of the view data
|
|
84
|
+
*/
|
|
85
|
+
viewData(key, value) {
|
|
86
|
+
return (sails.inertia.sharedViewData[key] = value)
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get view data
|
|
91
|
+
* @param {string} key - The key of the view data to get
|
|
92
|
+
* @returns {*} - The view data
|
|
93
|
+
*/
|
|
94
|
+
getViewData(key) {
|
|
95
|
+
return sails.inertia.sharedViewData[key] ?? sails.inertia.sharedViewData
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Create an optional prop
|
|
100
|
+
* This allows you to define properties that are only evaluated when accessed.
|
|
101
|
+
* @docs https://docs.sailscasts.com/boring-stack/partial-reloads#lazy-data-evaluation
|
|
102
|
+
* @param {Function} callback - The callback function to execute
|
|
103
|
+
* @returns {OptionalProp} - The optional prop
|
|
104
|
+
*/
|
|
105
|
+
optional(callback) {
|
|
106
|
+
return new OptionalProp(callback)
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Create a mergeable prop
|
|
111
|
+
* This allows you to merge multiple props together.
|
|
112
|
+
* @docs https://docs.sailscasts.com/boring-stack/merging-props
|
|
113
|
+
* @param {Function} callback - The callback function to execute
|
|
114
|
+
* @returns {MergeProp} - The mergeable prop
|
|
115
|
+
*/
|
|
116
|
+
merge(callback) {
|
|
117
|
+
return new MergeProp(callback)
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Create an always prop
|
|
122
|
+
* Always props are resolved on every request, whether partial or not.
|
|
123
|
+
* @docs https://docs.sailscasts.com/boring-stack/partial-reloads#lazy-data-evaluation
|
|
124
|
+
* @param {Function} callback - The callback function
|
|
125
|
+
* @returns {AlwaysProp} - The always prop
|
|
126
|
+
*/
|
|
127
|
+
always(callback) {
|
|
128
|
+
return new AlwaysProp(callback)
|
|
129
|
+
},
|
|
130
|
+
/**
|
|
131
|
+
* Create a deferred prop
|
|
132
|
+
* This allows you to load certain page data after the initial render.
|
|
133
|
+
* @docs https://docs.sailscasts.com/boring-stack/deferred-props
|
|
134
|
+
* @param {Function} cb - The callback function to execute
|
|
135
|
+
* @param {string} group - The group name
|
|
136
|
+
* @returns {DeferProp} - The deferred prop
|
|
137
|
+
*/
|
|
138
|
+
defer(cb, group = 'default') {
|
|
139
|
+
return new DeferProp(cb, group)
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Render the response
|
|
144
|
+
* @param {Object} req - The request object
|
|
145
|
+
* @param {Object} res - The response object
|
|
146
|
+
* @param {Object} data - The data to render
|
|
147
|
+
* @returns {*} - The rendered response
|
|
148
|
+
*/
|
|
149
|
+
render(req, res, data) {
|
|
150
|
+
return render(req, res, data)
|
|
151
|
+
},
|
|
152
|
+
/**
|
|
153
|
+
* Handle Inertia redirects
|
|
154
|
+
* See https://docs.sailscasts.com/boring-stack/redirects
|
|
155
|
+
* @param {Object} req - The request object
|
|
156
|
+
* @param {Object} res - The response object
|
|
157
|
+
* @param {string} url - The URL to redirect to
|
|
158
|
+
* @returns {Object} - The response object with the redirect
|
|
159
|
+
*/
|
|
160
|
+
location(req, res, url) {
|
|
161
|
+
return location(req, res, url)
|
|
162
|
+
},
|
|
50
163
|
|
|
51
|
-
|
|
52
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Encrypt history
|
|
166
|
+
* @docs https://docs.sailscasts.com/boring-stack/history-encryption
|
|
167
|
+
* @param {boolean} encrypt - Whether to encrypt the history
|
|
168
|
+
*/
|
|
169
|
+
encryptHistory(encrypt = true) {
|
|
170
|
+
sails.inertia.shouldEncryptHistory = encrypt
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Clear history state.
|
|
175
|
+
* @docs https://docs.sailscasts.com/boring-stack/history-encryption#clearing-history
|
|
176
|
+
*/
|
|
177
|
+
clearHistory() {
|
|
178
|
+
sails.inertia.shouldClearHistory = true
|
|
179
|
+
},
|
|
180
|
+
handleBadRequest(req, res, optionaLData) {
|
|
181
|
+
return handleBadRequest(req, res, optionaLData)
|
|
182
|
+
}
|
|
53
183
|
}
|
|
54
184
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
module.exports = function handleBadRequest(req, res, optionalData) {
|
|
3
|
+
const sails = req._sails
|
|
4
|
+
// Define the status code to send in the response.
|
|
5
|
+
const statusCodeToSet = 400
|
|
6
|
+
|
|
7
|
+
// Check if it's an Inertia request
|
|
8
|
+
if (req.header('X-Inertia')) {
|
|
9
|
+
if (optionalData && optionalData.problems) {
|
|
10
|
+
const errors = {}
|
|
11
|
+
optionalData.problems.forEach((problem) => {
|
|
12
|
+
if (typeof problem === 'object') {
|
|
13
|
+
Object.keys(problem).forEach((propertyName) => {
|
|
14
|
+
const sanitizedProblem = problem[propertyName].replace(/\.$/, '') // Trim trailing dot
|
|
15
|
+
if (!errors[propertyName]) {
|
|
16
|
+
errors[propertyName] = [sanitizedProblem]
|
|
17
|
+
} else {
|
|
18
|
+
errors[propertyName].push(sanitizedProblem)
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
} else {
|
|
22
|
+
const regex = /"(.*?)"/
|
|
23
|
+
const matches = problem.match(regex)
|
|
24
|
+
|
|
25
|
+
if (matches && matches.length > 1) {
|
|
26
|
+
const propertyName = matches[1]
|
|
27
|
+
const sanitizedProblem = problem
|
|
28
|
+
.replace(/"([^"]+)"/, '$1')
|
|
29
|
+
.replace('\n', '')
|
|
30
|
+
.replace('·', '')
|
|
31
|
+
.trim()
|
|
32
|
+
if (!errors[propertyName]) {
|
|
33
|
+
errors[propertyName] = [sanitizedProblem]
|
|
34
|
+
} else {
|
|
35
|
+
errors[propertyName].push(sanitizedProblem)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
req.session.errors = errors
|
|
41
|
+
return res.redirect(303, req.get('Referrer') || '/')
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// If not an Inertia request, perform the normal badRequest response
|
|
46
|
+
if (optionalData === undefined) {
|
|
47
|
+
sails.log.info('Ran custom response: res.badRequest()')
|
|
48
|
+
return res.sendStatus(statusCodeToSet)
|
|
49
|
+
} else if (_.isError(optionalData)) {
|
|
50
|
+
sails.log.info(
|
|
51
|
+
'Custom response `res.badRequest()` called with an Error:',
|
|
52
|
+
optionalData
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
if (!_.isFunction(optionalData.toJSON)) {
|
|
56
|
+
if (process.env.NODE_ENV === 'production') {
|
|
57
|
+
return res.sendStatus(statusCodeToSet)
|
|
58
|
+
} else {
|
|
59
|
+
return res.status(statusCodeToSet).send(optionalData.stack)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
return res.status(statusCodeToSet).send(optionalData)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const pickPropsToResolve = require('../props/pick-props-to-resolve')
|
|
2
|
+
const resolveDeferredProps = require('../props/resolve-deferred-props')
|
|
3
|
+
const resolveMergeProps = require('../props/resolve-merge-props')
|
|
4
|
+
const resolvePageProps = require('../props/resolve-page-props')
|
|
5
|
+
|
|
6
|
+
module.exports = async function buildPageObject(req, component, pageProps) {
|
|
7
|
+
const sails = req._sails
|
|
8
|
+
let url = req.url || req.originalUrl
|
|
9
|
+
const assetVersion = sails.config.inertia.version
|
|
10
|
+
const currentVersion =
|
|
11
|
+
typeof assetVersion === 'function' ? assetVersion() : assetVersion
|
|
12
|
+
const propsToResolve = pickPropsToResolve(req, component, {
|
|
13
|
+
...sails.inertia.sharedProps,
|
|
14
|
+
...pageProps
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
component,
|
|
19
|
+
url,
|
|
20
|
+
version: currentVersion,
|
|
21
|
+
props: await resolvePageProps(propsToResolve),
|
|
22
|
+
clearHistory: sails.inertia.shouldClearHistory,
|
|
23
|
+
encryptHistory: sails.inertia.shouldEncryptHistory,
|
|
24
|
+
...resolveMergeProps(req, pageProps),
|
|
25
|
+
...resolveDeferredProps(req, component, pageProps)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = Symbol('ignoreFirstLoad')
|
|
@@ -2,6 +2,9 @@ module.exports = {
|
|
|
2
2
|
INERTIA: 'X-Inertia',
|
|
3
3
|
VERSION: 'X-Inertia-Version',
|
|
4
4
|
PARTIAL_DATA: 'X-Inertia-Partial-Data',
|
|
5
|
+
PARTIAL_EXCEPT: 'X-Inertia-Partial-Except',
|
|
6
|
+
ERROR_BAG: 'X-Inertia-Error-Bag',
|
|
7
|
+
RESET: 'X-Inertia-Reset',
|
|
5
8
|
PARTIAL_COMPONENT: 'X-Inertia-Partial-Component',
|
|
6
9
|
LOCATION: 'X-Inertia-Location'
|
|
7
10
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
const { ERROR_BAG } = require('./inertia-headers')
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* @module resolveValidationErrors
|
|
3
6
|
* @description Resolves and formats validation errors from the session for Inertia responses.
|
|
@@ -22,7 +25,7 @@ module.exports = function resolveValidationErrors(req) {
|
|
|
22
25
|
return result
|
|
23
26
|
}, {})
|
|
24
27
|
|
|
25
|
-
const inertiaErrorBag = req.headers[
|
|
28
|
+
const inertiaErrorBag = req.headers[ERROR_BAG]
|
|
26
29
|
|
|
27
30
|
if (inertiaErrorBag && collectedErrors[inertiaErrorBag]) {
|
|
28
31
|
const selectedErrors = {
|
package/lib/location.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const { INERTIA, LOCATION } = require('./helpers/inertia-headers')
|
|
2
|
+
|
|
3
|
+
module.exports = function (req, res, url) {
|
|
4
|
+
if (req.get(INERTIA)) {
|
|
5
|
+
// If the method is PUT, PATCH, or DELETE, force a 303 redirect (so the next request is a GET)
|
|
6
|
+
if (['PUT', 'PATCH', 'DELETE'].includes(req.method)) {
|
|
7
|
+
return res.redirect(303, url)
|
|
8
|
+
}
|
|
9
|
+
// If this is an Inertia request, send a 409 Conflict response
|
|
10
|
+
// with the X-Inertia-Location header so the client performs a full window.location visit.
|
|
11
|
+
res.set(LOCATION, url)
|
|
12
|
+
return res.status(409).end()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Otherwise, perform a standard redirect (default is typically a 302)
|
|
16
|
+
return res.redirect(url)
|
|
17
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const isInertiaRequest = require('
|
|
1
|
+
const isInertiaRequest = require('../helpers/is-inertia-request')
|
|
2
2
|
|
|
3
|
-
const resolveValidationErrors = require('
|
|
3
|
+
const resolveValidationErrors = require('../helpers/resolve-validation-errors')
|
|
4
4
|
function inertia(hook) {
|
|
5
5
|
return function inertiaMiddleware(req, res, next) {
|
|
6
6
|
if (isInertiaRequest(req)) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const ignoreFirstLoadSymbol = require('../helpers/ignore-first-load-symbol')
|
|
2
|
+
const MergeableProp = require('./mergeable-prop')
|
|
3
|
+
|
|
4
|
+
module.exports = class DeferProp extends MergeableProp {
|
|
5
|
+
constructor(callback, group) {
|
|
6
|
+
super()
|
|
7
|
+
this.callback = callback
|
|
8
|
+
this.group = group
|
|
9
|
+
this[ignoreFirstLoadSymbol] = true
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getGroup() {
|
|
13
|
+
return this.group
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const isInertiaPartialRequest = require('../helpers/is-inertia-partial-request')
|
|
2
|
+
const ignoreFirstLoadSymbol = require('../helpers/ignore-first-load-symbol')
|
|
3
|
+
const { PARTIAL_DATA, PARTIAL_EXCEPT } = require('../helpers/inertia-headers')
|
|
4
|
+
const resolveOnlyProps = require('./resolve-only-props')
|
|
5
|
+
const resolveExceptProps = require('./resolve-except-props')
|
|
6
|
+
const AlwaysProp = require('./always-prop')
|
|
7
|
+
|
|
8
|
+
module.exports = function pickPropsToResolve(req, component, props = {}) {
|
|
9
|
+
const isPartial = isInertiaPartialRequest(req, component)
|
|
10
|
+
let newProps = props
|
|
11
|
+
|
|
12
|
+
if (!isPartial) {
|
|
13
|
+
newProps = Object.fromEntries(
|
|
14
|
+
Object.entries(props).filter(([_, value]) => {
|
|
15
|
+
if (value && value[ignoreFirstLoadSymbol]) return false
|
|
16
|
+
return true
|
|
17
|
+
})
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (isPartial && req.get(PARTIAL_DATA)) {
|
|
22
|
+
newProps = resolveOnlyProps(req, newProps)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (isPartial && req.get(PARTIAL_EXCEPT)) {
|
|
26
|
+
newProps = resolveExceptProps(req, newProps)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for (const [key, value] of Object.entries(props)) {
|
|
30
|
+
if (value instanceof AlwaysProp) newProps[key] = props[key]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return newProps
|
|
34
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const isInertiaPartialRequest = require('../helpers/is-inertia-partial-request')
|
|
2
|
+
const DeferProp = require('./defer-prop')
|
|
3
|
+
module.exports = function resolveDeferredProps(req, component, pageProps) {
|
|
4
|
+
if (isInertiaPartialRequest(req, component)) return {}
|
|
5
|
+
const deferredProps = Object.entries(pageProps || {})
|
|
6
|
+
.filter(([_, value]) => value instanceof DeferProp)
|
|
7
|
+
.map(([key, value]) => ({ key, group: value.getGroup() }))
|
|
8
|
+
.reduce((groups, { key, group }) => {
|
|
9
|
+
if (!groups[group]) groups[group] = []
|
|
10
|
+
groups[group].push(key)
|
|
11
|
+
return groups
|
|
12
|
+
}, {})
|
|
13
|
+
|
|
14
|
+
return Object.keys(deferredProps).length ? { deferredProps } : {}
|
|
15
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const { PARTIAL_EXCEPT } = require('../helpers/inertia-headers')
|
|
2
|
+
module.exports = function resolveExceptProps(req, props) {
|
|
3
|
+
const partialExceptHeader = req.get(PARTIAL_EXCEPT)
|
|
4
|
+
const except = partialExceptHeader.split(',').filter(Boolean)
|
|
5
|
+
|
|
6
|
+
for (const key of except) delete props[key]
|
|
7
|
+
|
|
8
|
+
return props
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { RESET } = require('../helpers/inertia-headers')
|
|
2
|
+
const MergeableProp = require('./mergeable-prop')
|
|
3
|
+
module.exports = function resolveMergeProps(req, pageProps) {
|
|
4
|
+
const inertiaResetHeader = req.get(RESET)
|
|
5
|
+
const resetProps = new Set(inertiaResetHeader?.split(',').filter(Boolean))
|
|
6
|
+
|
|
7
|
+
const mergeProps = Object.entries(pageProps || {})
|
|
8
|
+
.filter(([_, value]) => value instanceof MergeableProp && value.shouldMerge)
|
|
9
|
+
.map(([key]) => key)
|
|
10
|
+
.filter((key) => !resetProps.has(key))
|
|
11
|
+
|
|
12
|
+
return mergeProps.length ? { mergeProps } : {}
|
|
13
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const { PARTIAL_DATA } = require('../helpers/inertia-headers')
|
|
2
|
+
/**
|
|
3
|
+
* Extracts only the props specified by the partial header from the given props object.
|
|
4
|
+
*
|
|
5
|
+
* This function reads the header defined by the PARTIAL_DATA constant from the request,
|
|
6
|
+
* which should contain a comma-separated list of property keys. It then constructs and returns
|
|
7
|
+
* a new object that includes only those keys from the provided props object.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} req - The Express-style request object. It must have a `get` method to retrieve headers.
|
|
10
|
+
* @param {Object} props - The complete set of props.
|
|
11
|
+
* @returns {Object} An object containing only the properties whose keys were specified in the partial header.
|
|
12
|
+
*/
|
|
13
|
+
module.exports = function resolveOnlyProps(req, props) {
|
|
14
|
+
const partialOnlyHeader = req.get(PARTIAL_DATA)
|
|
15
|
+
const only = partialOnlyHeader.split(',').filter(Boolean)
|
|
16
|
+
let newProps = {}
|
|
17
|
+
|
|
18
|
+
for (const key of only) newProps[key] = props[key]
|
|
19
|
+
|
|
20
|
+
return newProps
|
|
21
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const resolveProp = require('./resolve-prop')
|
|
2
|
+
/**
|
|
3
|
+
* Resolve all page props.
|
|
4
|
+
*
|
|
5
|
+
* This function iterates over each property in the given object.
|
|
6
|
+
* If a property is a function, it is invoked with the provided context.
|
|
7
|
+
* Then, every property is passed to resolveProp() to see if it needs
|
|
8
|
+
* any special handling.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} [props={}] - An object containing page props.
|
|
11
|
+
* @returns {Promise<Object>} A promise that resolves to a new object with resolved props.
|
|
12
|
+
*/
|
|
13
|
+
module.exports = async function resolvePageProps(props = {}) {
|
|
14
|
+
const entries = await Promise.all(
|
|
15
|
+
Object.entries(props).map(async ([key, value]) => {
|
|
16
|
+
if (typeof value === 'function') {
|
|
17
|
+
const result = await value()
|
|
18
|
+
return resolveProp(key, result)
|
|
19
|
+
}
|
|
20
|
+
return resolveProp(key, value)
|
|
21
|
+
})
|
|
22
|
+
)
|
|
23
|
+
return Object.fromEntries(entries)
|
|
24
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const OptionalProp = require('./optional-prop')
|
|
2
|
+
const MergeProp = require('./merge-prop')
|
|
3
|
+
const DeferProp = require('./defer-prop')
|
|
4
|
+
const AlwaysProp = require('./always-prop')
|
|
5
|
+
/**
|
|
6
|
+
* Resolve a single prop.
|
|
7
|
+
*
|
|
8
|
+
* If the value is an instance of one of our special prop types,
|
|
9
|
+
* we assume it has a "callback" property that should be executed
|
|
10
|
+
* to retrieve the final value.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} key - The key for the prop.
|
|
13
|
+
* @param {any} value - The value to resolve.
|
|
14
|
+
* @returns {Promise<[string, any]>} A promise that resolves to a key-value pair.
|
|
15
|
+
*/
|
|
16
|
+
module.exports = async function resolveProp(key, value) {
|
|
17
|
+
if (
|
|
18
|
+
value instanceof OptionalProp ||
|
|
19
|
+
value instanceof MergeProp ||
|
|
20
|
+
value instanceof DeferProp ||
|
|
21
|
+
value instanceof AlwaysProp
|
|
22
|
+
) {
|
|
23
|
+
return [key, await value.callback()]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return [key, value]
|
|
27
|
+
}
|
package/lib/render.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const { encode } = require('querystring')
|
|
2
|
+
const inertiaHeaders = require('./helpers/inertia-headers')
|
|
3
|
+
const buildPageObject = require('./helpers/build-page-object')
|
|
4
|
+
|
|
5
|
+
module.exports = async function render(req, res, data) {
|
|
6
|
+
const sails = req._sails
|
|
7
|
+
|
|
8
|
+
const sharedViewData = sails.inertia.sharedViewData
|
|
9
|
+
const rootView = sails.config.inertia.rootView
|
|
10
|
+
|
|
11
|
+
const allViewData = {
|
|
12
|
+
...sharedViewData,
|
|
13
|
+
...data.viewData
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let page = await buildPageObject(req, data.page, data.props)
|
|
17
|
+
|
|
18
|
+
const queryParams = req.query
|
|
19
|
+
if (req.method === 'GET' && Object.keys(queryParams).length) {
|
|
20
|
+
// Keep original request query params
|
|
21
|
+
page.url += `?${encode(queryParams)}`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (req.get(inertiaHeaders.INERTIA)) {
|
|
25
|
+
res.set(inertiaHeaders.INERTIA, true)
|
|
26
|
+
res.set('Vary', 'Accept')
|
|
27
|
+
return res.json(page)
|
|
28
|
+
} else {
|
|
29
|
+
// Implements full page reload
|
|
30
|
+
return res.view(rootView, {
|
|
31
|
+
page,
|
|
32
|
+
viewData: allViewData
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "inertia-sails",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "The Sails adapter for Inertia.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"sails": {
|
|
@@ -8,13 +8,12 @@
|
|
|
8
8
|
"hookName": "inertia"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
-
"prepare": "cd .. && husky install"
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
13
12
|
},
|
|
14
13
|
"repository": {
|
|
15
14
|
"type": "git",
|
|
16
15
|
"url": "git+https://github.com/sailscastshq/boring-stack.git",
|
|
17
|
-
"directory": "inertia-sails"
|
|
16
|
+
"directory": "packages/inertia-sails"
|
|
18
17
|
},
|
|
19
18
|
"peerDependencies": {
|
|
20
19
|
"sails": ">=1",
|
|
@@ -29,5 +28,6 @@
|
|
|
29
28
|
"bugs": {
|
|
30
29
|
"url": "https://github.com/sailscastshq/boring-stack/issues"
|
|
31
30
|
},
|
|
32
|
-
"homepage": "https://github.com/sailscastshq/boring-stack/inertia-sails#readme"
|
|
31
|
+
"homepage": "https://github.com/sailscastshq/boring-stack/tree/main/inertia-sails#readme",
|
|
32
|
+
"devDependencies": {}
|
|
33
33
|
}
|
package/inertia-sails-0.2.0.tgz
DELETED
|
Binary file
|
|
File without changes
|
|
File without changes
|