@quazardous/qdadm 2.17.1 → 2.17.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quazardous/qdadm",
3
- "version": "2.17.1",
3
+ "version": "2.17.2",
4
4
  "description": "Vue 3 framework for admin dashboards with PrimeVue",
5
5
  "author": "quazardous",
6
6
  "license": "MIT",
@@ -54,7 +54,7 @@
54
54
  "dist/node"
55
55
  ],
56
56
  "dependencies": {
57
- "@quazardous/qdcore": "^1.1.1",
57
+ "@quazardous/qdcore": "^1.1.2",
58
58
  "@quazardous/qddebug": "^1.1.0",
59
59
  "@quazardous/quarkernel": "^2.1.0",
60
60
  "yaml": "^2.8.4"
@@ -15,6 +15,7 @@ import { StackHydrator } from '../chain/StackHydrator.js'
15
15
  import { Orchestrator } from '../orchestrator/Orchestrator'
16
16
  import type { EntityAuthAdapter } from '../entity/auth/EntityAuthAdapter'
17
17
  import type { Kernel } from './Kernel'
18
+ import type { SSEConfig } from './Kernel.types'
18
19
  // #1196 Phase B — this-typing against the real Kernel shape (was Self = any)
19
20
  type Self = Kernel
20
21
 
@@ -217,12 +218,72 @@ export function applyRegistryMethods(KernelClass: { prototype: Kernel }): void {
217
218
  /**
218
219
  * Create SSEBridge for Server-Sent Events to SignalBus integration
219
220
  */
221
+ /**
222
+ * Warn about unrecognised `sse` keys (#1898 lot A).
223
+ *
224
+ * A consumer configured `sse.getToken` against a version that predated it.
225
+ * It was not rejected — it was ignored, and the bridge fell back to the
226
+ * session's DURABLE token, which then travelled in the query string of every
227
+ * `/events` request and into logs that outlive the session by months. No
228
+ * error, no warning, and a stream that worked perfectly.
229
+ *
230
+ * The warning therefore names what happens INSTEAD, not merely that the key
231
+ * was ignored: "ignored" reads as "no effect", not as "falls back to a more
232
+ * sensitive secret". That reading is what made the failure invisible.
233
+ */
234
+ proto._validateSseConfig = function (this: Self, sse: SSEConfig): void {
235
+ const known = new Set([
236
+ 'url',
237
+ 'reconnectDelay',
238
+ 'signalPrefix',
239
+ 'autoConnect',
240
+ 'withCredentials',
241
+ 'tokenParam',
242
+ 'events',
243
+ 'entities',
244
+ 'getToken',
245
+ 'connectOnSignal',
246
+ 'disconnectOnSignal',
247
+ ])
248
+
249
+ const unknown = Object.keys(sse).filter((key) => !known.has(key))
250
+ if (!unknown.length) return
251
+
252
+ // What the app loses by having the key ignored, wherever we can say it.
253
+ const consequences: Record<string, string> = {
254
+ getToken:
255
+ 'the session auth token will be sent in the stream URL instead — ' +
256
+ 'and query strings reach access logs',
257
+ entities: 'no entity cache will be invalidated from the stream',
258
+ tokenParam: 'the token will be sent under the default name "token"',
259
+ }
260
+
261
+ for (const key of unknown) {
262
+ const near = [...known].find(
263
+ (k) => k.toLowerCase() === key.toLowerCase() || k.startsWith(key) || key.startsWith(k)
264
+ )
265
+ // Look the consequence up under the NEAREST KNOWN key: a typo like
266
+ // `getTokens` means `getToken` is absent, and it is that absence whose
267
+ // effect the reader needs to hear.
268
+ const consequence = consequences[key] ?? (near ? consequences[near] : undefined)
269
+ const because = consequence ? ` — ${consequence}` : ''
270
+ const suggestion = near ? ` Did you mean "${near}"?` : ''
271
+ console.warn(
272
+ `[Kernel] sse.${key} is not a recognised option and is IGNORED${because}.` +
273
+ `${suggestion} This usually means the installed qdadm predates the option ` +
274
+ `— check the version before assuming the key has no effect.`
275
+ )
276
+ }
277
+ }
278
+
220
279
  proto._createSSEBridge = function (this: Self): void {
221
280
  const { sse, authAdapter } = this.options
222
281
  if (!sse?.url) return
223
282
 
224
283
  const debug = this.options.debug ?? false
225
284
 
285
+ this._validateSseConfig(sse)
286
+
226
287
  // An explicit sse.getToken wins — including an explicit null, which means
227
288
  // "send no token" and must not fall back to the auth adapter.
228
289
  const getToken =
@@ -259,6 +320,29 @@ export function applyRegistryMethods(KernelClass: { prototype: Kernel }): void {
259
320
  })
260
321
  }
261
322
 
323
+ // A restored session must connect the stream too (#1898 lot C).
324
+ //
325
+ // The bridge waits for `auth:login`, which is emitted by the LOGIN PAGE —
326
+ // by the interface, not by the authentication state. So reloading a page
327
+ // with a valid session, or revalidating one without going through our
328
+ // login form, left the stream permanently disconnected.
329
+ //
330
+ // Deliberately NOT fixed by emitting `auth:login` at boot: the kernel
331
+ // listens to it in _setupAuthInvalidation and remounts the whole app, so
332
+ // announcing a restored session that way would remount on every reload.
333
+ // Connecting the bridge directly says the same thing without the blast
334
+ // radius.
335
+ // `in`, not `??`: an EXPLICIT null means "I drive the connection myself",
336
+ // and `??` would treat it as absent and override the opt-out.
337
+ const connectsOnSignal =
338
+ ('connectOnSignal' in sse ? sse.connectOnSignal : 'auth:login') !== null
339
+ if (!sse.autoConnect && connectsOnSignal && authAdapter?.isAuthenticated?.()) {
340
+ if (debug) {
341
+ console.debug('[Kernel] session already authenticated → connecting SSE')
342
+ }
343
+ void this.sseBridge.connect()
344
+ }
345
+
262
346
  if (sse.entities) {
263
347
  this.liveEntityRouter = createLiveEntityRouter({
264
348
  signals: this.signals!,
@@ -51,6 +51,7 @@ import type {
51
51
  KernelOptions,
52
52
  InternalLayoutComponents,
53
53
  AxiosLikeClient,
54
+ SSEConfig as SSEConfigType,
54
55
  } from './Kernel.types'
55
56
 
56
57
  // Re-export public types
@@ -440,6 +441,7 @@ export interface Kernel {
440
441
  _createStackHydrator(): void
441
442
  _createDeferredRegistry(): void
442
443
  _createEventRouter(): void
444
+ _validateSseConfig(sse: SSEConfigType): void
443
445
  _createSSEBridge(): void
444
446
 
445
447
  // Vue (Kernel.vue.ts)