inertia-sails 1.3.0 → 1.3.2

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 CHANGED
@@ -110,6 +110,38 @@ 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
@@ -127,19 +159,36 @@ module.exports = function defineInertiaHook(sails) {
127
159
  },
128
160
 
129
161
  /**
130
- * Hook routes - sets up AsyncLocalStorage context early so other hooks
131
- * can use sails.inertia.share() with proper request-scoped context.
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) => requestContext.run(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
+ }
175
+ },
176
+ 'POST /*': (req, res, next) => {
177
+ if (requestContext.getContext()) return next()
178
+ requestContext.run(req, res, next)
138
179
  },
139
- 'POST /*': (req, res, next) => requestContext.run(req, res, next),
140
- 'PUT /*': (req, res, next) => requestContext.run(req, res, next),
141
- 'PATCH /*': (req, res, next) => requestContext.run(req, res, next),
142
- 'DELETE /*': (req, res, next) => requestContext.run(req, res, next)
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
- // Fallback to global share if called outside request (e.g., in hooks)
162
- sails.inertia.globalSharedProps[key] = value
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,27 +242,24 @@ module.exports = function defineInertiaHook(sails) {
190
242
 
191
243
  /**
192
244
  * Flush shared properties for the current request.
193
- * Since context is set up early in routes.before, this always works
194
- * in hooks and middleware.
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, global = false) {
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
- if (global) {
205
- delete sails.inertia.globalSharedProps[key]
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
- if (global) {
212
- sails.inertia.globalSharedProps = {}
213
- }
262
+ sails.inertia.globalSharedProps = {}
214
263
  }
215
264
  },
216
265
 
@@ -227,8 +276,10 @@ module.exports = function defineInertiaHook(sails) {
227
276
  requestContext.setSharedLocal(key, value)
228
277
  return value
229
278
  }
230
- // Fallback to global if called outside request
231
- sails.inertia.globalSharedLocals[key] = value
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
 
@@ -46,7 +46,7 @@ module.exports = {
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
  /**
package/lib/render.js CHANGED
@@ -32,6 +32,15 @@ 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, { page, ...allLocals })
35
+ //
36
+ // We pass locals both as top-level properties AND nested under a `locals`
37
+ // key. This is necessary because Sails's default EJS renderer creates an
38
+ // `options.locals` object (for blocks, layout, partial helpers). EJS wraps
39
+ // templates in `with(data) { ... }`, so inside the template `locals`
40
+ // resolves to `data.locals` (the nested object) rather than the `locals`
41
+ // function parameter. By pre-populating `data.locals` with our values,
42
+ // `<%= locals.title %>` in the EJS template correctly resolves to the
43
+ // dynamic value instead of undefined.
44
+ return res.view(rootView, { page, ...allLocals, locals: { ...allLocals } })
36
45
  }
37
46
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "inertia-sails",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "The Sails adapter for Inertia.",
5
5
  "main": "index.js",
6
6
  "sails": {