@cat-factory/app 0.46.5 → 0.46.7

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.
@@ -49,6 +49,13 @@ export function createApiClient(): WretchInstance {
49
49
  const apiBase = useRuntimeConfig().public.apiBase
50
50
  return wretch(apiBase).middlewares([
51
51
  (next) => async (url, opts) => {
52
+ // Tag every request with this tab's stable connection id (matched to the `?cid=` on
53
+ // the realtime WebSocket) so the backend can skip echoing a board mutation's coarse
54
+ // event back to the connection that caused it — see `utils/connectionId.ts`.
55
+ opts.headers = {
56
+ ...(opts.headers as Record<string, string> | undefined),
57
+ 'X-Connection-Id': connectionId(),
58
+ }
52
59
  const token = useAuthStore().token
53
60
  if (token) {
54
61
  opts.headers = {
@@ -119,7 +119,11 @@ export function useWorkspaceStream() {
119
119
  // A workspace switch (or stop()) may have happened while awaiting the mint.
120
120
  if (stopped || workspace.workspaceId !== workspaceId) return
121
121
 
122
- const query = ticket ? `?ticket=${encodeURIComponent(ticket)}` : ''
122
+ // Carry this tab's stable connection id so the backend can suppress echoing a board
123
+ // mutation's coarse event back to the connection that caused it (same id the api
124
+ // client sends as `X-Connection-Id`) — see `utils/connectionId.ts`.
125
+ const cid = `cid=${encodeURIComponent(connectionId())}`
126
+ const query = ticket ? `?ticket=${encodeURIComponent(ticket)}&${cid}` : `?${cid}`
123
127
  socket = new WebSocket(`${wsBase}/workspaces/${workspaceId}/events${query}`)
124
128
 
125
129
  socket.onopen = () => {
package/app/stores/ui.ts CHANGED
@@ -411,7 +411,7 @@ export const useUiStore = defineStore('ui', () => {
411
411
  // panel opened outside the hubs never grows a dead Back control, and so switching from
412
412
  // one hub's panel to the other's clears the stale marker.
413
413
  function resetHubReturn() {
414
- resetHubReturn()
414
+ cameFromIntegrations.value = false
415
415
  cameFromPersonal.value = false
416
416
  }
417
417
  function openIntegrations() {
@@ -0,0 +1,21 @@
1
+ /**
2
+ * A stable per-tab connection id, generated once on first use. It rides on every REST
3
+ * request (the `X-Connection-Id` header, added in the api client) AND on the realtime
4
+ * WebSocket connect (`?cid=`), so the backend can recognise the connection that caused a
5
+ * board mutation and skip echoing the resulting coarse `board` event back to it.
6
+ *
7
+ * Without this, a client consumes the WebSocket event for its OWN move and runs a
8
+ * debounced full board refresh; a snapshot fetched mid-flight (between two rapid drags)
9
+ * carries a stale position, so the block snaps back to where it was after the previous
10
+ * move. Suppressing the self-echo leaves the originating client on its optimistic state +
11
+ * its own REST response (already the freshest), while other clients still refresh.
12
+ */
13
+ let cached: string | null = null
14
+
15
+ export function connectionId(): string {
16
+ if (cached) return cached
17
+ cached =
18
+ globalThis.crypto?.randomUUID?.() ??
19
+ `cid-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`
20
+ return cached
21
+ }
package/nuxt.config.ts CHANGED
@@ -81,6 +81,24 @@ export default defineNuxtConfig({
81
81
  join(layerDir, 'app/assets/css/main.css'),
82
82
  ],
83
83
 
84
+ // Force Vite to pre-bundle `fast-querystring` (a CommonJS dep reached via
85
+ // @toad-contracts/frontend-http-client). Left un-optimized, Vite serves its raw CJS
86
+ // from @fs where cjs-module-lexer can't see the named `stringify` export (the module
87
+ // uses a `module.exports = x; module.exports.stringify = …` reassignment), so the SPA
88
+ // throws at runtime:
89
+ // SyntaxError: … fast-querystring/lib/index.js does not provide an export named 'stringify'
90
+ // Pre-bundling makes esbuild emit an ESM wrapper with proper CJS interop.
91
+ //
92
+ // This config is resolved from the CONSUMER app's root (the deployment that `extends`
93
+ // this layer), where — under pnpm's strict layout — only this layer (`@cat-factory/app`)
94
+ // is hoisted; `frontend-http-client`/`fast-querystring` are not. So anchor the nested
95
+ // `a > b > c` specifier at `@cat-factory/app` and let Vite resolve each hop from there.
96
+ vite: {
97
+ optimizeDeps: {
98
+ include: ['@cat-factory/app > @toad-contracts/frontend-http-client > fast-querystring'],
99
+ },
100
+ },
101
+
84
102
  app: {
85
103
  head: {
86
104
  title: 'Agent Architecture Board',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.46.5",
3
+ "version": "0.46.7",
4
4
  "description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
5
5
  "repository": {
6
6
  "type": "git",