@burdenoff/fe-libs 2026.1023.6 → 2026.1023.8

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.
Files changed (40) hide show
  1. package/dist/shared/assistant/gatewayClient.js +23 -19
  2. package/dist/shared/assistant/index.d.ts +1 -0
  3. package/dist/shared/assistant/productContext.d.ts +16 -0
  4. package/dist/shared/assistant/productContext.js +16 -0
  5. package/dist/shared/graphql/client.d.ts +9 -0
  6. package/dist/shared/graphql/client.js +241 -201
  7. package/dist/shared/graphql/fetch.js +135 -123
  8. package/dist/shared/graphql/index.d.ts +1 -0
  9. package/dist/shared/graphql/workspaceOperationGuard.d.ts +70 -0
  10. package/dist/shared/graphql/workspaceOperationGuard.js +62 -0
  11. package/dist/shared/providers/AppShellProvider.d.ts +16 -0
  12. package/dist/shared/providers/AppShellProvider.js +258 -251
  13. package/dist/shared/providers/shell/AuthProvider.d.ts +14 -1
  14. package/dist/shared/providers/shell/AuthProvider.js +105 -94
  15. package/dist/shared/providers/shell/MultiGatewayProvider.d.ts +8 -1
  16. package/dist/shared/providers/shell/MultiGatewayProvider.js +115 -119
  17. package/dist/shared/providers/shell/ProfileStorageManager.js +1 -1
  18. package/dist/shared/providers/shell/WorkspaceWriteRefusedToastListener.d.ts +4 -0
  19. package/dist/shared/providers/shell/WorkspaceWriteRefusedToastListener.js +23 -0
  20. package/dist/shared/providers/shell/authProvider/identityChange.d.ts +26 -0
  21. package/dist/shared/providers/shell/authProvider/identityChange.js +11 -0
  22. package/dist/shared/providers/shell/authProvider/useAuthActions.d.ts +4 -1
  23. package/dist/shared/providers/shell/authProvider/useAuthActions.js +105 -92
  24. package/dist/shared/providers/shell/authProvider/useBridgeInitialization.d.ts +27 -1
  25. package/dist/shared/providers/shell/authProvider/useBridgeInitialization.js +82 -62
  26. package/dist/shared/providers/shell/authProvider/useRemoteSLO.d.ts +4 -1
  27. package/dist/shared/providers/shell/authProvider/useRemoteSLO.js +16 -14
  28. package/dist/shared/providers/shell/authProvider/useStorageSync.d.ts +4 -1
  29. package/dist/shared/providers/shell/authProvider/useStorageSync.js +19 -14
  30. package/dist/shared/providers/shell/contextSwitchCache.d.ts +4 -0
  31. package/dist/shared/providers/shell/contextSwitchCache.js +17 -0
  32. package/dist/shared/providers/shell/identityQueryReset.d.ts +17 -0
  33. package/dist/shared/providers/shell/identityQueryReset.js +21 -0
  34. package/dist/shared/providers/shell/identityScopedQueryReset.d.ts +16 -0
  35. package/dist/shared/providers/shell/identityScopedQueryReset.js +30 -0
  36. package/dist/shared/providers/shell/index.d.ts +1 -1
  37. package/dist/shared-assistant.js +306 -305
  38. package/dist/shared-graphql.js +11 -10
  39. package/dist/shared.js +129 -128
  40. package/package.json +2 -2
@@ -6,14 +6,15 @@ import { getActiveWorkspaceToken as i, resolveActiveProfileId as a, setProfileWo
6
6
  import { readActiveContextValueWithUrlPrecedence as s } from "../providers/shell/ActiveContextStorage.js";
7
7
  import { getValidAccessTokenFromStorage as c, sanitizeExtraHeaders as l, sanitizeGeography as u, validateGatewayBaseUrl as d } from "./security.js";
8
8
  import { isWorkspaceTokenValidForContext as f } from "./workspaceToken.js";
9
- import { dispatchBackendErrorEvent as p, markConnectionHealthy as m } from "../utils/backendErrors.js";
9
+ import { isSameWorkspaceId as p } from "./workspaceOperationGuard.js";
10
+ import { dispatchBackendErrorEvent as m, markConnectionHealthy as h } from "../utils/backendErrors.js";
10
11
  //#region src/shared/graphql/fetch.ts
11
- var h = "burdenoff:session-expired";
12
- function g(e) {
13
- typeof window > "u" || window.dispatchEvent(new CustomEvent(h, { detail: e }));
12
+ var g = "burdenoff:session-expired";
13
+ function _(e) {
14
+ typeof window > "u" || window.dispatchEvent(new CustomEvent(g, { detail: e }));
14
15
  }
15
- var _ = "\n mutation IssueWorkspaceToken($input: IssueWorkspaceTokenInput!) {\n issueWorkspaceToken(input: $input) {\n success\n token\n workspaceId\n error {\n message\n }\n }\n }\n", v = /* @__PURE__ */ new Map();
16
- function y() {
16
+ var v = "\n mutation IssueWorkspaceToken($input: IssueWorkspaceTokenInput!) {\n issueWorkspaceToken(input: $input) {\n success\n token\n workspaceId\n error {\n message\n }\n }\n }\n", y = /* @__PURE__ */ new Map();
17
+ function b() {
17
18
  try {
18
19
  let e = sessionStorage.getItem("bf-active-profile");
19
20
  return e ? c(localStorage.getItem(`bf-p-${e}-tokens`)) : null;
@@ -21,21 +22,21 @@ function y() {
21
22
  return null;
22
23
  }
23
24
  }
24
- function b() {
25
+ function x() {
25
26
  try {
26
27
  return i();
27
28
  } catch {
28
29
  return null;
29
30
  }
30
31
  }
31
- function x() {
32
+ function S() {
32
33
  try {
33
34
  return sessionStorage.getItem("bf-active-profile");
34
35
  } catch {
35
36
  return null;
36
37
  }
37
38
  }
38
- function S() {
39
+ function C() {
39
40
  try {
40
41
  let e = sessionStorage.getItem("bf-active-profile");
41
42
  if (!e) return !1;
@@ -45,7 +46,7 @@ function S() {
45
46
  return !1;
46
47
  }
47
48
  }
48
- function C(e) {
49
+ function w(e) {
49
50
  try {
50
51
  let [, t] = e.split(".");
51
52
  if (!t || typeof globalThis.atob != "function") return null;
@@ -55,11 +56,11 @@ function C(e) {
55
56
  return null;
56
57
  }
57
58
  }
58
- function w(e) {
59
- let t = C(e)?.exp;
59
+ function ee(e) {
60
+ let t = w(e)?.exp;
60
61
  return typeof t == "number" ? t * 1e3 <= Date.now() + 3e4 : !1;
61
62
  }
62
- function ee() {
63
+ function te() {
63
64
  try {
64
65
  let e = sessionStorage.getItem("bf-active-profile");
65
66
  if (e) {
@@ -103,7 +104,7 @@ function A() {
103
104
  return null;
104
105
  }
105
106
  }
106
- function te(e) {
107
+ function ne(e) {
107
108
  return {
108
109
  workspaceId: typeof e.workspaceId == "string" ? e.workspaceId : E() || void 0,
109
110
  organizationId: typeof e.organizationId == "string" ? e.organizationId : D() || A() || void 0,
@@ -113,12 +114,19 @@ function te(e) {
113
114
  }
114
115
  function j(e, t, n) {
115
116
  try {
116
- let r = a() ?? x();
117
+ let r = a() ?? S();
117
118
  if (!r) return;
118
119
  o(r, t), sessionStorage.setItem("burdenoff-active-context-workspace", e), n && sessionStorage.setItem("burdenoff-active-context-organization", n);
119
120
  } catch {}
120
121
  }
121
- function M(e, t, n) {
122
+ function M() {
123
+ try {
124
+ return a() ?? S();
125
+ } catch {
126
+ return null;
127
+ }
128
+ }
129
+ function N(e, t, n) {
122
130
  return t ? t.aborted ? Promise.resolve(n) : new Promise((r) => {
123
131
  let i = () => r(n);
124
132
  t.addEventListener("abort", i, { once: !0 }), e.then(r, () => r(n)).finally(() => {
@@ -126,29 +134,27 @@ function M(e, t, n) {
126
134
  });
127
135
  }) : e;
128
136
  }
129
- async function N(e) {
137
+ async function P(e) {
130
138
  let { url: t, authToken: n, workspaceId: r, organizationId: i, projectId: a, locale: o, signal: s, currentQuery: c, issuanceGuard: l } = e;
131
139
  if (!n || c.includes("issueWorkspaceToken") || l && !l.isCurrent()) return null;
132
140
  let u = r || E();
133
141
  if (!u) return null;
134
- let d = b(), p = E(), m = i || D() || A();
135
- if (d && p === u && f(d, {
142
+ let d = x(), m = E(), h = i || D() || A();
143
+ if (d && m === u && f(d, {
136
144
  workspaceId: u,
137
- organizationId: m,
145
+ organizationId: h,
138
146
  projectId: a
139
147
  }) && (!l || l.accepts(d, u))) return d;
140
- let h = JSON.stringify([
141
- m ?? "",
148
+ let g = M(), _ = JSON.stringify([
149
+ h ?? "",
142
150
  u,
143
151
  a ?? "",
144
- l ? [
145
- t,
146
- n,
147
- l.identityKey
148
- ] : null
149
- ]), g = v.get(h);
150
- if (g) return M(g, s, null);
151
- let y = fetch(t, {
152
+ g ?? "",
153
+ n,
154
+ l ? [t, l.identityKey] : null
155
+ ]), b = y.get(_);
156
+ if (b) return N(b, s, null);
157
+ let S = fetch(t, {
152
158
  method: "POST",
153
159
  headers: {
154
160
  "Content-Type": "application/json",
@@ -157,78 +163,84 @@ async function N(e) {
157
163
  ...o ? { "Accept-Language": o } : {}
158
164
  },
159
165
  body: JSON.stringify({
160
- query: _,
166
+ query: v,
161
167
  variables: { input: {
162
168
  workspaceId: u,
163
- ...m ? { organizationId: m } : {},
169
+ ...h ? { organizationId: h } : {},
164
170
  ...a ? { projectId: a } : {}
165
171
  } }
166
172
  })
167
173
  }).then(async (e) => {
168
174
  if (l && !e.ok) return null;
169
175
  let t = await e.json(), n = t.data?.issueWorkspaceToken;
170
- return l && (t.errors !== void 0 && (!Array.isArray(t.errors) || t.errors.length !== 0) || n?.success !== !0 || typeof n.token != "string" || n.token.length === 0 || n.workspaceId !== u) ? null : n?.success && n.token ? l && (!l.isCurrent() || !l.accepts(n.token, n.workspaceId || u)) ? null : (j(n.workspaceId || u, n.token, m ?? void 0), n.token) : null;
176
+ if (l && (t.errors !== void 0 && (!Array.isArray(t.errors) || t.errors.length !== 0) || n?.success !== !0 || typeof n.token != "string" || n.token.length === 0 || n.workspaceId !== u)) return null;
177
+ if (n?.success && n.token) {
178
+ if (l && (!l.isCurrent() || !l.accepts(n.token, n.workspaceId || u))) return null;
179
+ let e = n.workspaceId || u;
180
+ return p(e, E()) && M() === g && j(e, n.token, h ?? void 0), n.token;
181
+ }
182
+ return null;
171
183
  }).catch(() => null).finally(() => {
172
- v.delete(h);
184
+ y.delete(_);
173
185
  });
174
- return v.set(h, y), M(y, s, null);
186
+ return y.set(_, S), N(S, s, null);
175
187
  }
176
- function P(e, n) {
188
+ function F(e, n) {
177
189
  let i = r[e], a = t(), o = e === "global" ? a.globalBaseUrl : a.workspaceBaseUrl, s = n || o;
178
190
  return `${s.endsWith("/") ? s.slice(0, -1) : s}${i.graphql}`;
179
191
  }
180
- function F(e) {
192
+ function I(e) {
181
193
  return e.reason ?? new DOMException("The operation was aborted.", "AbortError");
182
194
  }
183
- function I(e) {
184
- if (e?.aborted) throw F(e);
195
+ function L(e) {
196
+ if (e?.aborted) throw I(e);
185
197
  }
186
- function L() {
198
+ function R() {
187
199
  return globalThis;
188
200
  }
189
- function R() {
190
- let e = L();
201
+ function z() {
202
+ let e = R();
191
203
  return e.__graphqlFetchQueues ||= /* @__PURE__ */ new Map(), e.__graphqlFetchQueues;
192
204
  }
193
- function z() {
194
- let e = L();
205
+ function B() {
206
+ let e = R();
195
207
  return e.__graphqlFetchResponseCache ||= /* @__PURE__ */ new Map(), e.__graphqlFetchResponseCache;
196
208
  }
197
- function B() {
198
- let e = L();
209
+ function V() {
210
+ let e = R();
199
211
  return e.__graphqlFetchPendingResponses ||= /* @__PURE__ */ new Map(), e.__graphqlFetchPendingResponses;
200
212
  }
201
- var V = Symbol.for("@burdenoff/fe-libs:graphql-fetch-cache-hit");
202
- function H(e) {
203
- return !!(e && typeof e == "object" && e[V] === !0);
213
+ var H = Symbol.for("@burdenoff/fe-libs:graphql-fetch-cache-hit");
214
+ function re(e) {
215
+ return !!(e && typeof e == "object" && e[H] === !0);
204
216
  }
205
- function ne() {
206
- z().clear(), B().clear(), n();
217
+ function ie() {
218
+ B().clear(), V().clear(), n();
207
219
  }
208
- var re = 10, ie = 600, U = 100, ae = 1e4, W = 15e3, oe = new Set([
220
+ var ae = 10, oe = 600, U = 100, se = 1e4, W = 15e3, ce = new Set([
209
221
  "traceparent",
210
222
  "tracestate",
211
223
  "baggage"
212
224
  ]), G = "Apollo-Require-Preflight";
213
- function se() {
225
+ function le() {
214
226
  if (typeof performance > "u") return U;
215
- let e = L();
216
- return e.__graphqlFetchBurstStartedAt ??= performance.now(), performance.now() - e.__graphqlFetchBurstStartedAt < ae ? ie : U;
227
+ let e = R();
228
+ return e.__graphqlFetchBurstStartedAt ??= performance.now(), performance.now() - e.__graphqlFetchBurstStartedAt < se ? oe : U;
217
229
  }
218
- function ce(e) {
230
+ function ue(e) {
219
231
  return typeof e.query == "string" ? /^\s*query\b/.test(e.query) : !1;
220
232
  }
221
- function le(e) {
233
+ function de(e) {
222
234
  return typeof e.operationName == "string" && e.operationName.length > 0 ? {
223
235
  operationName: e.operationName,
224
236
  variables: e.variables ?? null
225
237
  } : e;
226
238
  }
227
- function ue(e) {
239
+ function fe(e) {
228
240
  let t = /* @__PURE__ */ new Map();
229
241
  for (let [n, r] of Object.entries(e)) {
230
242
  let e = n.toLowerCase();
231
- oe.has(e) || t.set(e, r);
243
+ ce.has(e) || t.set(e, r);
232
244
  }
233
245
  return Object.fromEntries(Array.from(t.entries()).sort(([e], [t]) => e.localeCompare(t)));
234
246
  }
@@ -241,13 +253,13 @@ function K(t) {
241
253
  };
242
254
  }
243
255
  function q(e) {
244
- return de(e) && Array.isArray(e.errors);
256
+ return pe(e) && Array.isArray(e.errors);
245
257
  }
246
258
  function J(e) {
247
259
  let t = e.status;
248
260
  return t === 401 ? "Invalid email or password." : t === 403 ? "You do not have permission to do that." : t === 404 ? "We could not find what you were looking for." : t === 408 || t === 504 ? "The server took too long to respond. Please try again." : t === 429 ? "Too many requests. Please wait a moment and try again." : t >= 500 && t < 600 ? "The server is having trouble right now. Please try again in a moment." : t >= 400 && t < 500 ? "The request could not be processed. Please refresh and try again." : "We could not reach the server. Please check your connection and try again.";
249
261
  }
250
- function de(e) {
262
+ function pe(e) {
251
263
  return typeof e == "object" && !!e && !Array.isArray(e);
252
264
  }
253
265
  async function Y(e) {
@@ -272,7 +284,7 @@ async function X(e, t, n) {
272
284
  ...r.keepalive ? { keepalive: !0 } : {}
273
285
  }), i = await Y(n);
274
286
  if (!n.ok && !q(i)) throw Error(J(n));
275
- r.cacheKey && !q(i) && z().set(r.cacheKey, {
287
+ r.cacheKey && !q(i) && B().set(r.cacheKey, {
276
288
  value: i,
277
289
  expiresAt: Date.now() + W
278
290
  }), r.resolve(i);
@@ -292,7 +304,7 @@ async function X(e, t, n) {
292
304
  if (!i.ok && !q(a)) throw Error(J(i));
293
305
  if (Array.isArray(a)) a.forEach((e, t) => {
294
306
  let r = n[t];
295
- r && (r.cacheKey && !q(e) && z().set(r.cacheKey, {
307
+ r && (r.cacheKey && !q(e) && B().set(r.cacheKey, {
296
308
  value: e,
297
309
  expiresAt: Date.now() + W
298
310
  }), r.resolve(e));
@@ -306,18 +318,18 @@ async function X(e, t, n) {
306
318
  }
307
319
  }
308
320
  function Z(e, t, n, r, i) {
309
- let a = ue(t), o = i?.keepalive === !0, s = o ? 1 : Math.max(1, i?.batchMax ?? re), c = Math.max(0, i?.batchInterval ?? se()), l = `${e}|${JSON.stringify(a)}`, u = ce(n) ? `${l}|${JSON.stringify(le(n))}` : void 0;
310
- if (r?.aborted) return Promise.reject(F(r));
321
+ let a = fe(t), o = i?.keepalive === !0, s = o ? 1 : Math.max(1, i?.batchMax ?? ae), c = Math.max(0, i?.batchInterval ?? le()), l = `${e}|${JSON.stringify(a)}`, u = ue(n) ? `${l}|${JSON.stringify(de(n))}` : void 0;
322
+ if (r?.aborted) return Promise.reject(I(r));
311
323
  if (u) {
312
- let e = z(), t = e.get(u);
324
+ let e = B(), t = e.get(u);
313
325
  if (t) {
314
326
  if (t.expiresAt > Date.now()) {
315
327
  let e = Promise.resolve(t.value);
316
- return e[V] = !0, e;
328
+ return e[H] = !0, e;
317
329
  }
318
330
  e.delete(u);
319
331
  }
320
- let n = B().get(u);
332
+ let n = V().get(u);
321
333
  if (n) return n;
322
334
  }
323
335
  let d = new Promise((t, i) => {
@@ -332,7 +344,7 @@ function Z(e, t, n, r, i) {
332
344
  }]);
333
345
  return;
334
346
  }
335
- let d = R(), f = d.get(l);
347
+ let d = z(), f = d.get(l);
336
348
  f || (f = {
337
349
  requests: [],
338
350
  timer: null,
@@ -347,115 +359,115 @@ function Z(e, t, n, r, i) {
347
359
  cacheKey: u
348
360
  };
349
361
  if (f.requests.push(p), r && r.addEventListener("abort", () => {
350
- let e = R().get(l), t = e ? e.requests.indexOf(p) : -1;
351
- e && t >= 0 && (e.requests.splice(t, 1), e.requests.length === 0 && (e.timer && clearTimeout(e.timer), R().delete(l))), i(F(r));
362
+ let e = z().get(l), t = e ? e.requests.indexOf(p) : -1;
363
+ e && t >= 0 && (e.requests.splice(t, 1), e.requests.length === 0 && (e.timer && clearTimeout(e.timer), z().delete(l))), i(I(r));
352
364
  }, { once: !0 }), f.requests.length >= f.batchMax) {
353
365
  Q(e, a, l);
354
366
  return;
355
367
  }
356
368
  f.timer ||= setTimeout(() => Q(e, a, l), f.batchInterval);
357
369
  });
358
- return u && !r && (B().set(u, d), d.finally(() => {
359
- B().delete(u);
370
+ return u && !r && (V().set(u, d), d.finally(() => {
371
+ V().delete(u);
360
372
  }).catch(() => {})), d;
361
373
  }
362
374
  function Q(e, t, n) {
363
- let r = R(), i = r.get(n);
375
+ let r = z(), i = r.get(n);
364
376
  i && (r.delete(n), i.timer &&= (clearTimeout(i.timer), null), X(e, t, i.requests));
365
377
  }
366
378
  async function $(e) {
367
- let { gateway: n, query: r, variables: i, operationName: a, authToken: o, workspaceToken: s, workspaceId: c, organizationId: u, tenantId: h, projectId: _, locale: v, baseUrl: x, extraHeaders: C, signal: E, suppressGlobalErrorEvent: D, keepalive: k } = e;
368
- I(E);
369
- let A = d(P(n, x)), j = o && !w(o) ? o : y();
370
- if (!j && !e.skipAuthRetry && typeof window < "u" && !window.location.pathname.startsWith("/auth/") && S()) {
379
+ let { gateway: n, query: r, variables: i, operationName: a, authToken: o, workspaceToken: s, workspaceId: c, organizationId: u, tenantId: p, projectId: g, locale: v, baseUrl: y, extraHeaders: S, signal: w, suppressGlobalErrorEvent: E, keepalive: D } = e;
380
+ L(w);
381
+ let k = d(F(n, y)), A = o && !ee(o) ? o : b();
382
+ if (!A && !e.skipAuthRetry && typeof window < "u" && !window.location.pathname.startsWith("/auth/") && C()) {
371
383
  let e = await import("../utils/authErrorHandling.js");
372
384
  await e.attemptTokenRefresh({
373
- globalBaseUrl: n === "global" && x ? x : t().globalBaseUrl,
385
+ globalBaseUrl: n === "global" && y ? y : t().globalBaseUrl,
374
386
  ...e.getSharedTokenRefreshRefs()
375
- }) ? j = y() : g({
387
+ }) ? A = b() : _({
376
388
  operationName: a,
377
389
  gateway: n
378
- }), I(E);
390
+ }), L(w);
379
391
  }
380
- let M = v ?? T(), F = te({
392
+ let j = v ?? T(), M = ne({
381
393
  workspaceId: c,
382
394
  organizationId: u,
383
- tenantId: h,
384
- projectId: _
385
- }), L = {
395
+ tenantId: p,
396
+ projectId: g
397
+ }), N = {
386
398
  "Content-Type": "application/json",
387
399
  [G]: "true"
388
400
  };
389
- j && (L.Authorization = `Bearer ${j}`), M && (L["Accept-Language"] = M);
390
- let R = ee();
391
- if (R?.country && (L["X-Geo-Country"] = R.country), R?.currency && (L["X-Geo-Currency"] = R.currency), R?.timezone && (L["X-Geo-Timezone"] = R.timezone), n === "workspace" ? s !== !1 : s) {
392
- let e = typeof s == "string" ? s : b();
393
- e && !f(e, F) && (e = null), !e && (s === !0 || n === "workspace" && s === void 0 || typeof s == "string") && (e = await N({
394
- url: A,
395
- authToken: j,
396
- workspaceId: F.workspaceId,
397
- organizationId: F.organizationId,
398
- projectId: F.projectId,
399
- locale: M,
400
- signal: E,
401
+ A && (N.Authorization = `Bearer ${A}`), j && (N["Accept-Language"] = j);
402
+ let I = te();
403
+ if (I?.country && (N["X-Geo-Country"] = I.country), I?.currency && (N["X-Geo-Currency"] = I.currency), I?.timezone && (N["X-Geo-Timezone"] = I.timezone), n === "workspace" ? s !== !1 : s) {
404
+ let e = typeof s == "string" ? s : x();
405
+ e && !f(e, M) && (e = null), !e && (s === !0 || n === "workspace" && s === void 0 || typeof s == "string") && (e = await P({
406
+ url: k,
407
+ authToken: A,
408
+ workspaceId: M.workspaceId,
409
+ organizationId: M.organizationId,
410
+ projectId: M.projectId,
411
+ locale: j,
412
+ signal: w,
401
413
  currentQuery: r
402
- }), I(E)), e && (L["X-Workspace-Authorization"] = `Bearer ${e}`);
414
+ }), L(w)), e && (N["X-Workspace-Authorization"] = `Bearer ${e}`);
403
415
  }
404
416
  if (n === "workspace" || c) {
405
- let e = F.workspaceId;
406
- e && (L["x-workspace-id"] = e);
417
+ let e = M.workspaceId;
418
+ e && (N["x-workspace-id"] = e);
407
419
  }
408
420
  if (n === "global" || u) {
409
- let e = F.organizationId;
410
- e && (L["x-org-id"] = e);
421
+ let e = M.organizationId;
422
+ e && (N["x-org-id"] = e);
411
423
  }
412
- if (h) {
413
- let e = typeof h == "string" ? h : O();
414
- e && (L["x-tenant-id"] = e);
424
+ if (p) {
425
+ let e = typeof p == "string" ? p : O();
426
+ e && (N["x-tenant-id"] = e);
415
427
  }
416
- if (n === "workspace" || _) {
417
- let e = F.projectId;
418
- e && (L["x-project-id"] = e);
428
+ if (n === "workspace" || g) {
429
+ let e = M.projectId;
430
+ e && (N["x-project-id"] = e);
419
431
  }
420
- let z = l(C);
421
- z && Object.assign(L, z);
422
- let B = { query: r };
423
- i && (B.variables = i), a && (B.operationName = a);
424
- let V = async (r) => {
425
- if (e.skipAuthRetry || !j || typeof window > "u" || !r?.length) return null;
432
+ let R = l(S);
433
+ R && Object.assign(N, R);
434
+ let z = { query: r };
435
+ i && (z.variables = i), a && (z.operationName = a);
436
+ let B = async (r) => {
437
+ if (e.skipAuthRetry || !A || typeof window > "u" || !r?.length) return null;
426
438
  let i = await import("../utils/authErrorHandling.js");
427
439
  return i.hasAuthGraphQLErrors(r) ? await i.attemptTokenRefresh({
428
- globalBaseUrl: n === "global" && x ? x : t().globalBaseUrl,
440
+ globalBaseUrl: n === "global" && y ? y : t().globalBaseUrl,
429
441
  ...i.getSharedTokenRefreshRefs()
430
442
  }) ? $({
431
443
  ...e,
432
444
  authToken: void 0,
433
445
  workspaceToken: typeof s == "string" ? !0 : s,
434
446
  skipAuthRetry: !0
435
- }) : (g({
447
+ }) : (_({
436
448
  operationName: a,
437
449
  gateway: n
438
450
  }), null) : null;
439
451
  };
440
452
  try {
441
- let e = Z(A, L, B, E, k ? { keepalive: !0 } : void 0), t = await e;
442
- if (H(e) || m(), t.errors?.length) {
443
- let e = await V(t.errors);
453
+ let e = Z(k, N, z, w, D ? { keepalive: !0 } : void 0), t = await e;
454
+ if (re(e) || h(), t.errors?.length) {
455
+ let e = await B(t.errors);
444
456
  if (e) return e;
445
- D || p({ errors: t.errors }, { operationName: a });
457
+ E || m({ errors: t.errors }, { operationName: a });
446
458
  }
447
459
  return t;
448
460
  } catch (e) {
449
461
  let t = e.errors;
450
462
  if (t?.length) {
451
- let e = await V(t);
463
+ let e = await B(t);
452
464
  if (e) return e;
453
465
  }
454
- throw D || p(e, {
466
+ throw E || m(e, {
455
467
  operationName: a,
456
468
  fallbackMessage: "We could not reach the server. Please check your connection and try again."
457
469
  }), e;
458
470
  }
459
471
  }
460
472
  //#endregion
461
- export { h as SESSION_EXPIRED_EVENT, ne as clearGraphqlFetchResponseCache, Z as enqueueGraphQLFetch, N as ensureWorkspaceToken, y as getStoredAuthToken, T as getStoredLocale, D as getStoredOrganizationId, k as getStoredProjectId, O as getStoredTenantId, E as getStoredWorkspaceId, b as getStoredWorkspaceToken, $ as graphqlFetch, P as resolveGatewayUrl };
473
+ export { g as SESSION_EXPIRED_EVENT, ie as clearGraphqlFetchResponseCache, Z as enqueueGraphQLFetch, P as ensureWorkspaceToken, b as getStoredAuthToken, T as getStoredLocale, D as getStoredOrganizationId, k as getStoredProjectId, O as getStoredTenantId, E as getStoredWorkspaceId, x as getStoredWorkspaceToken, $ as graphqlFetch, F as resolveGatewayUrl };
@@ -1,6 +1,7 @@
1
1
  export { createGraphQLClient, initializeAppClients, getWorkspaceClient, getGlobalClient, resetAppClients, type CreateClientOptions, type AppClientConfig, type Gateway, } from './client';
2
2
  export { GatewayProvider, useGateway, useGatewayClient, useSafeGateway, useSafeGatewayClient, type GatewayContextValue, type GatewayProviderProps, } from './GatewayContext';
3
3
  export { graphqlFetch, clearGraphqlFetchResponseCache, getStoredAuthToken, getStoredWorkspaceToken, getStoredWorkspaceId, getStoredLocale, getStoredOrganizationId, getStoredProjectId, resolveGatewayUrl, type GraphQLFetchOptions, type GraphQLResponse, } from './fetch';
4
+ export { WORKSPACE_CONTEXT_CHANGED, WORKSPACE_CONTEXT_MISMATCH_EVENT, WORKSPACE_WRITE_REFUSED_EVENT, WORKSPACE_WRITE_REFUSED_MESSAGE, WorkspaceContextChangedError, isWorkspaceContextChangedError, readContextInputWorkspaceId, type WorkspaceContextChangedDetail, type WorkspaceOperationGuardMode, } from './workspaceOperationGuard';
4
5
  export { getWorkspaceTokenPayload, isWorkspaceTokenValidForContext, type WorkspaceTokenContext, type WorkspaceTokenPayload, } from './workspaceToken';
5
6
  export { useHasAuthToken } from './useHasAuthToken';
6
7
  export { SSELink, type SSELinkOptions } from './links/sse-link';
@@ -0,0 +1,70 @@
1
+ import { ApolloClient } from '@apollo/client/core';
2
+ import { DocumentNode } from 'graphql';
3
+ export declare const WORKSPACE_CONTEXT_CHANGED: "WORKSPACE_CONTEXT_CHANGED";
4
+ /** Dispatched on `window` once per refused mutation (guard `'enforce'`). */
5
+ export declare const WORKSPACE_WRITE_REFUSED_EVENT: "burdenoff:workspace-write-refused";
6
+ /** Dispatched on `window` per mismatching operation that is still sent (guard `'report'`). */
7
+ export declare const WORKSPACE_CONTEXT_MISMATCH_EVENT: "burdenoff:workspace-context-mismatch";
8
+ /** What the user is told when a save was refused (the shell toast's English fallback). */
9
+ export declare const WORKSPACE_WRITE_REFUSED_MESSAGE = "Not saved \u2014 workspace changed, switch back and try again";
10
+ export type WorkspaceOperationGuardMode = 'off' | 'report' | 'enforce';
11
+ export type GuardedOperationKind = 'query' | 'mutation' | 'subscription';
12
+ export type WorkspaceGuardPhase = 'before-send' | 'after-token-mint';
13
+ export interface WorkspaceContextChangedDetail {
14
+ operationName: string | null;
15
+ operationKind: GuardedOperationKind;
16
+ /** The workspace named in the operation's ContextInput. */
17
+ builtForWorkspaceId: string;
18
+ /** The workspace that was active when the operation would have been sent. */
19
+ activeWorkspaceId: string;
20
+ /** `after-token-mint`: the switch landed while a workspace token was being minted. */
21
+ phase: WorkspaceGuardPhase;
22
+ }
23
+ export declare class WorkspaceContextChangedError extends Error {
24
+ readonly code: "WORKSPACE_CONTEXT_CHANGED";
25
+ readonly extensions: {
26
+ code: "WORKSPACE_CONTEXT_CHANGED";
27
+ };
28
+ readonly detail: WorkspaceContextChangedDetail;
29
+ constructor(detail: WorkspaceContextChangedDetail);
30
+ }
31
+ /**
32
+ * True for a refusal by this guard. Duck-typed (name, code, `extensions.code`,
33
+ * then `cause`) so that an error from a second bundled copy of fe-libs, or one
34
+ * Apollo wrapped, still matches.
35
+ */
36
+ export declare function isWorkspaceContextChangedError(error: unknown): error is WorkspaceContextChangedError;
37
+ /** Only a mode the guard knows is honoured: anything else is `'off'`. */
38
+ export declare function normalizeWorkspaceOperationGuardMode(value: unknown): WorkspaceOperationGuardMode;
39
+ /**
40
+ * Workspace ids are UUIDs, so they compare trimmed and case-insensitively: a
41
+ * difference in spelling alone must never refuse an operation.
42
+ */
43
+ export declare function isSameWorkspaceId(a: string | null, b: string | null): boolean;
44
+ /** The workspace an operation was built for, from the ContextInput convention only. */
45
+ export declare function readContextInputWorkspaceId(variables: unknown): string | null;
46
+ export declare function operationKindOf(query: DocumentNode): GuardedOperationKind;
47
+ /**
48
+ * The mismatch, or null when the operation may be sent: it names no workspace,
49
+ * no workspace is active, or the two are the same.
50
+ */
51
+ export declare function findWorkspaceContextMismatch(operation: {
52
+ operationName?: string | null;
53
+ variables?: unknown;
54
+ query: DocumentNode;
55
+ }, activeWorkspaceId: string | null, phase: WorkspaceGuardPhase): WorkspaceContextChangedDetail | null;
56
+ /**
57
+ * For a refetch of active queries under the guard: false only for a query the
58
+ * guard would refuse (built for a workspace other than the active one). Such a
59
+ * query keeps showing its own workspace's data until its screen remounts.
60
+ */
61
+ export declare function isQueryForActiveWorkspace(observableQuery: {
62
+ variables?: unknown;
63
+ }, activeWorkspaceId: string | null): boolean;
64
+ /**
65
+ * Refetch a client's active queries. Under `'enforce'` it skips the queries
66
+ * the guard would refuse, so a refresh (after a switch, or after a save) never
67
+ * re-sends a screen built for the previous workspace. Otherwise it refetches
68
+ * every active query, as before.
69
+ */
70
+ export declare function refetchActiveQueries(client: Pick<ApolloClient, 'refetchQueries'>, guard: WorkspaceOperationGuardMode, activeWorkspaceId: () => string | null): Promise<unknown>;
@@ -0,0 +1,62 @@
1
+ //#region src/shared/graphql/workspaceOperationGuard.ts
2
+ var e = "WORKSPACE_CONTEXT_CHANGED", t = "burdenoff:workspace-write-refused", n = "burdenoff:workspace-context-mismatch", r = "Not saved — workspace changed, switch back and try again", i = "Not loaded — this request was built for a workspace that is no longer active", a = class extends Error {
3
+ code = e;
4
+ extensions = { code: e };
5
+ detail;
6
+ constructor(e) {
7
+ super(e.operationKind === "mutation" ? r : i), this.name = "WorkspaceContextChangedError", this.detail = e;
8
+ }
9
+ };
10
+ function o(e) {
11
+ let t = e;
12
+ for (let e = 0; e < 4; e += 1) {
13
+ if (t instanceof a) return !0;
14
+ if (!t || typeof t != "object") return !1;
15
+ let e = t;
16
+ if (e.name === "WorkspaceContextChangedError" || e.code === "WORKSPACE_CONTEXT_CHANGED" || e.extensions?.code === "WORKSPACE_CONTEXT_CHANGED") return !0;
17
+ if (e.cause === t) return !1;
18
+ t = e.cause;
19
+ }
20
+ return !1;
21
+ }
22
+ function s(e) {
23
+ return e === "report" || e === "enforce" ? e : "off";
24
+ }
25
+ function c(e) {
26
+ return typeof e == "string" && e.trim() ? e.trim() : null;
27
+ }
28
+ function l(e, t) {
29
+ let n = c(e), r = c(t);
30
+ return n !== null && r !== null && n.toLowerCase() === r.toLowerCase();
31
+ }
32
+ function u(e) {
33
+ if (!e || typeof e != "object") return null;
34
+ let t = e;
35
+ return c(t.context?.workspaceId) ?? c(t.input?.context?.workspaceId);
36
+ }
37
+ function d(e) {
38
+ for (let t of e.definitions) if (t.kind === "OperationDefinition") return t.operation;
39
+ return "query";
40
+ }
41
+ function f(e, t, n) {
42
+ let r = u(e.variables), i = c(t);
43
+ return !r || !i || l(r, i) ? null : {
44
+ operationName: e.operationName || null,
45
+ operationKind: d(e.query),
46
+ builtForWorkspaceId: r,
47
+ activeWorkspaceId: i,
48
+ phase: n
49
+ };
50
+ }
51
+ function p(e, t) {
52
+ let n = u(e.variables);
53
+ return !n || !c(t) || l(n, t);
54
+ }
55
+ function m(e, t, n) {
56
+ return t === "enforce" ? e.refetchQueries({
57
+ include: "active",
58
+ onQueryUpdated: (e) => p(e, n())
59
+ }) : e.refetchQueries({ include: "active" });
60
+ }
61
+ //#endregion
62
+ export { e as WORKSPACE_CONTEXT_CHANGED, n as WORKSPACE_CONTEXT_MISMATCH_EVENT, t as WORKSPACE_WRITE_REFUSED_EVENT, r as WORKSPACE_WRITE_REFUSED_MESSAGE, a as WorkspaceContextChangedError, f as findWorkspaceContextMismatch, l as isSameWorkspaceId, o as isWorkspaceContextChangedError, s as normalizeWorkspaceOperationGuardMode, u as readContextInputWorkspaceId, m as refetchActiveQueries };
@@ -1,4 +1,5 @@
1
1
  import { CSSProperties, ReactNode } from 'react';
2
+ import { WorkspaceOperationGuardMode } from '../graphql/workspaceOperationGuard';
2
3
  import { Brand, ThemeMode, Density, Typography, GlassIntensity } from './shell/ThemeProvider';
3
4
  import { TranslationDictionary } from './shell/I18nProvider';
4
5
  import { BurdenoffEventName } from '../events';
@@ -127,6 +128,21 @@ export interface AppShellConfig {
127
128
  tourRouteCatalog?: readonly TourRouteCatalogEntry[];
128
129
  /** Resolve workspace RBAC above the tour runtime for permission-targeted tours. */
129
130
  enableWorkspaceTourPermissions?: boolean;
131
+ /**
132
+ * BOFF-8106: what the workspace gateway client does with an operation whose
133
+ * ContextInput (`variables.context.workspaceId` or
134
+ * `variables.input.context.workspaceId`) names a workspace other than the
135
+ * active one.
136
+ * - `'off'` (default): send it, as before.
137
+ * - `'report'`: send it and dispatch `burdenoff:workspace-context-mismatch`
138
+ * (measure on alpha before enforcing).
139
+ * - `'enforce'`: do not send it. A refused save shows "Not saved — workspace
140
+ * changed, switch back and try again"; a refused read ends with a
141
+ * `WorkspaceContextChangedError` and no error toast. A switch no longer
142
+ * re-sends screens built for the previous workspace.
143
+ * Opt in only after the app sends no placeholder workspace in a ContextInput.
144
+ */
145
+ workspaceOperationGuard?: WorkspaceOperationGuardMode;
130
146
  }
131
147
  export interface AppShellObservabilityConfig {
132
148
  enabled?: boolean;