inertia-sails 1.2.0 → 1.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/README.md +4 -4
- package/index.js +91 -40
- package/lib/helpers/request-context.js +10 -10
- package/lib/render.js +6 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ Return an Inertia page response:
|
|
|
75
75
|
return {
|
|
76
76
|
page: 'users/index', // Component name
|
|
77
77
|
props: { users: [...] }, // Props passed to component
|
|
78
|
-
|
|
78
|
+
locals: { title: '...' } // Locals for root EJS template
|
|
79
79
|
}
|
|
80
80
|
```
|
|
81
81
|
|
|
@@ -106,12 +106,12 @@ Share data across all requests (app-wide):
|
|
|
106
106
|
sails.inertia.shareGlobally('appName', 'My App')
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
#### `
|
|
109
|
+
#### `local(key, value)`
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
Set a local variable for the root EJS template:
|
|
112
112
|
|
|
113
113
|
```js
|
|
114
|
-
sails.inertia.
|
|
114
|
+
sails.inertia.local('title', 'Dashboard')
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
### Once Props (Cached)
|
package/index.js
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* @typedef {Object} InertiaRenderData
|
|
31
31
|
* @property {string} page - The component name to render
|
|
32
32
|
* @property {Object.<string, *>} [props] - Props to pass to the component
|
|
33
|
-
* @property {Object.<string, *>} [
|
|
33
|
+
* @property {Object.<string, *>} [locals] - Additional locals for the root EJS template
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
/**
|
|
@@ -110,13 +110,45 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
},
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Configure phase — runs after all hooks' defaults are merged but BEFORE
|
|
116
|
+
* any hook's initialize(). This is the right place to inject HTTP middleware
|
|
117
|
+
* because the HTTP hook hasn't read the middleware order yet.
|
|
118
|
+
*/
|
|
119
|
+
configure: function () {
|
|
120
|
+
// Inject AsyncLocalStorage context middleware into the HTTP stack
|
|
121
|
+
// BEFORE the router. This guarantees context is available in ALL
|
|
122
|
+
// routes.before handlers from all hooks, regardless of hook load order.
|
|
123
|
+
//
|
|
124
|
+
// Lifecycle:
|
|
125
|
+
// cookieParser → session → bodyParser → ... → inertiaContext → router
|
|
126
|
+
//
|
|
127
|
+
// By the time any routes.before handler calls sails.inertia.share(),
|
|
128
|
+
// the request is already wrapped in AsyncLocalStorage context.
|
|
129
|
+
if (sails.config.http && sails.config.http.middleware) {
|
|
130
|
+
const mw = sails.config.http.middleware
|
|
131
|
+
if (mw.order && mw.order.indexOf('inertiaContext') === -1) {
|
|
132
|
+
const routerIdx = mw.order.indexOf('router')
|
|
133
|
+
if (routerIdx !== -1) {
|
|
134
|
+
mw.order.splice(routerIdx, 0, 'inertiaContext')
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!mw.inertiaContext) {
|
|
138
|
+
mw.inertiaContext = function inertiaContext(req, res, next) {
|
|
139
|
+
requestContext.run(req, res, next)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
|
|
113
145
|
initialize: async function () {
|
|
114
146
|
hook = this
|
|
115
147
|
sails.inertia = hook
|
|
116
148
|
// Global shared props (for app-wide data like app name, version)
|
|
117
149
|
// These are merged with request-scoped shares
|
|
118
150
|
sails.inertia.globalSharedProps = {}
|
|
119
|
-
sails.inertia.
|
|
151
|
+
sails.inertia.globalSharedLocals = {}
|
|
120
152
|
// Default history encryption from config
|
|
121
153
|
sails.inertia.defaultEncryptHistory = sails.config.inertia.history.encrypt
|
|
122
154
|
sails.on('router:before', function () {
|
|
@@ -127,19 +159,36 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
127
159
|
},
|
|
128
160
|
|
|
129
161
|
/**
|
|
130
|
-
* Hook routes
|
|
131
|
-
*
|
|
162
|
+
* Hook routes — fallback context setup for socket requests.
|
|
163
|
+
* HTTP requests already have context from the inertiaContext middleware.
|
|
164
|
+
* Socket requests bypass Express middleware, so they need this.
|
|
132
165
|
*/
|
|
133
166
|
routes: {
|
|
134
167
|
before: {
|
|
135
168
|
'GET /*': {
|
|
136
169
|
skipAssets: true,
|
|
137
|
-
fn: (req, res, next) =>
|
|
170
|
+
fn: (req, res, next) => {
|
|
171
|
+
// Skip if context already set up by HTTP middleware
|
|
172
|
+
if (requestContext.getContext()) return next()
|
|
173
|
+
requestContext.run(req, res, next)
|
|
174
|
+
}
|
|
138
175
|
},
|
|
139
|
-
'POST /*': (req, res, next) =>
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
176
|
+
'POST /*': (req, res, next) => {
|
|
177
|
+
if (requestContext.getContext()) return next()
|
|
178
|
+
requestContext.run(req, res, next)
|
|
179
|
+
},
|
|
180
|
+
'PUT /*': (req, res, next) => {
|
|
181
|
+
if (requestContext.getContext()) return next()
|
|
182
|
+
requestContext.run(req, res, next)
|
|
183
|
+
},
|
|
184
|
+
'PATCH /*': (req, res, next) => {
|
|
185
|
+
if (requestContext.getContext()) return next()
|
|
186
|
+
requestContext.run(req, res, next)
|
|
187
|
+
},
|
|
188
|
+
'DELETE /*': (req, res, next) => {
|
|
189
|
+
if (requestContext.getContext()) return next()
|
|
190
|
+
requestContext.run(req, res, next)
|
|
191
|
+
}
|
|
143
192
|
}
|
|
144
193
|
},
|
|
145
194
|
|
|
@@ -152,14 +201,17 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
152
201
|
* @returns {*} - The value that was shared
|
|
153
202
|
*/
|
|
154
203
|
share(key, value = null) {
|
|
155
|
-
// If we're in a request context, share for this request only
|
|
156
204
|
const context = requestContext.getContext()
|
|
157
205
|
if (context) {
|
|
158
206
|
requestContext.setSharedProp(key, value)
|
|
159
207
|
return value
|
|
160
208
|
}
|
|
161
|
-
//
|
|
162
|
-
|
|
209
|
+
// Never fall back to global — that causes data to leak across requests.
|
|
210
|
+
// Use shareGlobally() for truly global data like app name.
|
|
211
|
+
sails.log.warn(
|
|
212
|
+
`sails.inertia.share('${key}') called outside request context. ` +
|
|
213
|
+
'Value was not stored. Use shareGlobally() for global data.'
|
|
214
|
+
)
|
|
163
215
|
return value
|
|
164
216
|
},
|
|
165
217
|
|
|
@@ -190,67 +242,66 @@ module.exports = function defineInertiaHook(sails) {
|
|
|
190
242
|
|
|
191
243
|
/**
|
|
192
244
|
* Flush shared properties for the current request.
|
|
193
|
-
*
|
|
194
|
-
*
|
|
245
|
+
* Always flushes from both request-scoped AND global storage to prevent
|
|
246
|
+
* stale data from leaking across requests.
|
|
195
247
|
* @param {string|null} key - The key of the property to flush, or null to flush all
|
|
196
|
-
* @param {boolean} [global=false] - Whether to also flush global props (rarely needed)
|
|
197
248
|
*/
|
|
198
|
-
flushShared(key
|
|
249
|
+
flushShared(key) {
|
|
199
250
|
const context = requestContext.getContext()
|
|
200
251
|
if (key) {
|
|
201
252
|
if (context) {
|
|
202
253
|
delete context.sharedProps[key]
|
|
203
254
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
255
|
+
// Always clean global too — prevents stale data from a previous
|
|
256
|
+
// request (or a share() that fell through before context was ready)
|
|
257
|
+
delete sails.inertia.globalSharedProps[key]
|
|
207
258
|
} else {
|
|
208
259
|
if (context) {
|
|
209
260
|
context.sharedProps = {}
|
|
210
261
|
}
|
|
211
|
-
|
|
212
|
-
sails.inertia.globalSharedProps = {}
|
|
213
|
-
}
|
|
262
|
+
sails.inertia.globalSharedProps = {}
|
|
214
263
|
}
|
|
215
264
|
},
|
|
216
265
|
|
|
217
266
|
/**
|
|
218
|
-
*
|
|
267
|
+
* Set a local for the current request's root EJS template.
|
|
219
268
|
* Uses AsyncLocalStorage to ensure data doesn't leak between concurrent requests.
|
|
220
|
-
* @param {string} key - The
|
|
221
|
-
* @param {*} value - The value
|
|
269
|
+
* @param {string} key - The local variable name
|
|
270
|
+
* @param {*} value - The value
|
|
222
271
|
* @returns {*} - The value that was set
|
|
223
272
|
*/
|
|
224
|
-
|
|
273
|
+
local(key, value) {
|
|
225
274
|
const context = requestContext.getContext()
|
|
226
275
|
if (context) {
|
|
227
|
-
requestContext.
|
|
276
|
+
requestContext.setSharedLocal(key, value)
|
|
228
277
|
return value
|
|
229
278
|
}
|
|
230
|
-
|
|
231
|
-
|
|
279
|
+
sails.log.warn(
|
|
280
|
+
`sails.inertia.local('${key}') called outside request context. ` +
|
|
281
|
+
'Value was not stored. Use localGlobally() for global data.'
|
|
282
|
+
)
|
|
232
283
|
return value
|
|
233
284
|
},
|
|
234
285
|
|
|
235
286
|
/**
|
|
236
|
-
*
|
|
237
|
-
* @param {string} key - The
|
|
238
|
-
* @param {*} value - The value
|
|
287
|
+
* Set a local globally across all requests.
|
|
288
|
+
* @param {string} key - The local variable name
|
|
289
|
+
* @param {*} value - The value
|
|
239
290
|
* @returns {*} - The value that was set
|
|
240
291
|
*/
|
|
241
|
-
|
|
242
|
-
sails.inertia.
|
|
292
|
+
localGlobally(key, value) {
|
|
293
|
+
sails.inertia.globalSharedLocals[key] = value
|
|
243
294
|
return value
|
|
244
295
|
},
|
|
245
296
|
|
|
246
297
|
/**
|
|
247
|
-
* Get
|
|
248
|
-
* @param {string} key - The
|
|
249
|
-
* @returns {*} - The
|
|
298
|
+
* Get locals (merges global + request-scoped)
|
|
299
|
+
* @param {string} key - The local variable name to get
|
|
300
|
+
* @returns {*} - The locals
|
|
250
301
|
*/
|
|
251
|
-
|
|
252
|
-
const globalData = sails.inertia.
|
|
253
|
-
const requestData = requestContext.
|
|
302
|
+
getLocals(key) {
|
|
303
|
+
const globalData = sails.inertia.globalSharedLocals
|
|
304
|
+
const requestData = requestContext.getSharedLocals()
|
|
254
305
|
const merged = { ...globalData, ...requestData }
|
|
255
306
|
return key ? merged[key] : merged
|
|
256
307
|
},
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* req: Request,
|
|
12
12
|
* res: Response,
|
|
13
13
|
* sharedProps: {}, // Request-scoped shared props
|
|
14
|
-
*
|
|
14
|
+
* sharedLocals: {}, // Request-scoped locals for root EJS template
|
|
15
15
|
* encryptHistory: null, // Request-scoped history encryption (null = use default)
|
|
16
16
|
* clearHistory: false, // Request-scoped clear history flag
|
|
17
17
|
* refreshOnceProps: [], // Props to force-refresh for this request
|
|
@@ -40,13 +40,13 @@ module.exports = {
|
|
|
40
40
|
req,
|
|
41
41
|
res,
|
|
42
42
|
sharedProps: {},
|
|
43
|
-
|
|
43
|
+
sharedLocals: {},
|
|
44
44
|
encryptHistory: null,
|
|
45
45
|
clearHistory: false,
|
|
46
46
|
refreshOnceProps: [], // Props to force-refresh for this request
|
|
47
47
|
rootView: null // Request-scoped root view template
|
|
48
48
|
}
|
|
49
|
-
return requestContext.run(context, callback)
|
|
49
|
+
return requestContext.run(context, () => callback())
|
|
50
50
|
},
|
|
51
51
|
|
|
52
52
|
/**
|
|
@@ -97,23 +97,23 @@ module.exports = {
|
|
|
97
97
|
},
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
|
-
* Get request-scoped shared
|
|
101
|
-
* @returns {Object} - The shared
|
|
100
|
+
* Get request-scoped shared locals
|
|
101
|
+
* @returns {Object} - The shared locals for this request
|
|
102
102
|
*/
|
|
103
|
-
|
|
103
|
+
getSharedLocals() {
|
|
104
104
|
const context = requestContext.getStore()
|
|
105
|
-
return context?.
|
|
105
|
+
return context?.sharedLocals || {}
|
|
106
106
|
},
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
|
-
* Set request-scoped shared
|
|
109
|
+
* Set a request-scoped shared local
|
|
110
110
|
* @param {string} key - The key
|
|
111
111
|
* @param {*} value - The value
|
|
112
112
|
*/
|
|
113
|
-
|
|
113
|
+
setSharedLocal(key, value) {
|
|
114
114
|
const context = requestContext.getStore()
|
|
115
115
|
if (context) {
|
|
116
|
-
context.
|
|
116
|
+
context.sharedLocals[key] = value
|
|
117
117
|
}
|
|
118
118
|
},
|
|
119
119
|
|
package/lib/render.js
CHANGED
|
@@ -8,11 +8,11 @@ module.exports = async function render(req, res, data) {
|
|
|
8
8
|
// Use request-scoped rootView if set, otherwise fall back to config
|
|
9
9
|
const rootView = requestContext.getRootView() || sails.config.inertia.rootView
|
|
10
10
|
|
|
11
|
-
// Use request-scoped
|
|
12
|
-
// This prevents
|
|
13
|
-
const
|
|
14
|
-
...sails.inertia.
|
|
15
|
-
...data.
|
|
11
|
+
// Use request-scoped locals merged with global locals
|
|
12
|
+
// This prevents locals from leaking between concurrent requests
|
|
13
|
+
const allLocals = {
|
|
14
|
+
...sails.inertia.getLocals(), // Merges global + request-scoped
|
|
15
|
+
...data.locals
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
let page = await buildPageObject(req, data.page, data.props)
|
|
@@ -32,9 +32,6 @@ module.exports = async function render(req, res, data) {
|
|
|
32
32
|
return res.json(page)
|
|
33
33
|
} else {
|
|
34
34
|
// Implements full page reload
|
|
35
|
-
return res.view(rootView, {
|
|
36
|
-
page,
|
|
37
|
-
viewData: allViewData
|
|
38
|
-
})
|
|
35
|
+
return res.view(rootView, { page, ...allLocals })
|
|
39
36
|
}
|
|
40
37
|
}
|