@burdenoff/fe-libs 2026.1023.7 → 2026.1023.9
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/dist/shared/events/sinks/RybbitSink.d.ts +21 -0
- package/dist/shared/events/sinks/RybbitSink.js +36 -11
- package/dist/shared/events/sinks/index.d.ts +1 -1
- package/dist/shared/graphql/client.d.ts +9 -0
- package/dist/shared/graphql/client.js +241 -201
- package/dist/shared/graphql/fetch.js +135 -123
- package/dist/shared/graphql/index.d.ts +1 -0
- package/dist/shared/graphql/workspaceOperationGuard.d.ts +70 -0
- package/dist/shared/graphql/workspaceOperationGuard.js +62 -0
- package/dist/shared/providers/AppShellProvider.d.ts +16 -0
- package/dist/shared/providers/AppShellProvider.js +232 -226
- package/dist/shared/providers/shell/AuthProvider.d.ts +14 -1
- package/dist/shared/providers/shell/AuthProvider.js +105 -94
- package/dist/shared/providers/shell/MultiGatewayProvider.d.ts +8 -1
- package/dist/shared/providers/shell/MultiGatewayProvider.js +115 -119
- package/dist/shared/providers/shell/ProfileStorageManager.js +1 -1
- package/dist/shared/providers/shell/WorkspaceWriteRefusedToastListener.d.ts +4 -0
- package/dist/shared/providers/shell/WorkspaceWriteRefusedToastListener.js +23 -0
- package/dist/shared/providers/shell/authProvider/identityChange.d.ts +26 -0
- package/dist/shared/providers/shell/authProvider/identityChange.js +11 -0
- package/dist/shared/providers/shell/authProvider/useAuthActions.d.ts +4 -1
- package/dist/shared/providers/shell/authProvider/useAuthActions.js +105 -92
- package/dist/shared/providers/shell/authProvider/useBridgeInitialization.d.ts +27 -1
- package/dist/shared/providers/shell/authProvider/useBridgeInitialization.js +82 -62
- package/dist/shared/providers/shell/authProvider/useRemoteSLO.d.ts +4 -1
- package/dist/shared/providers/shell/authProvider/useRemoteSLO.js +16 -14
- package/dist/shared/providers/shell/authProvider/useStorageSync.d.ts +4 -1
- package/dist/shared/providers/shell/authProvider/useStorageSync.js +19 -14
- package/dist/shared/providers/shell/contextSwitchCache.d.ts +4 -0
- package/dist/shared/providers/shell/contextSwitchCache.js +17 -0
- package/dist/shared/providers/shell/identityQueryReset.d.ts +17 -0
- package/dist/shared/providers/shell/identityQueryReset.js +21 -0
- package/dist/shared/providers/shell/identityScopedQueryReset.d.ts +16 -0
- package/dist/shared/providers/shell/identityScopedQueryReset.js +30 -0
- package/dist/shared/providers/shell/index.d.ts +1 -1
- package/dist/shared-events.js +29 -29
- package/dist/shared-graphql.js +11 -10
- package/dist/shared.js +129 -128
- 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 {
|
|
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
|
|
12
|
-
function
|
|
13
|
-
typeof window > "u" || window.dispatchEvent(new CustomEvent(
|
|
12
|
+
var g = "burdenoff:session-expired";
|
|
13
|
+
function _(e) {
|
|
14
|
+
typeof window > "u" || window.dispatchEvent(new CustomEvent(g, { detail: e }));
|
|
14
15
|
}
|
|
15
|
-
var
|
|
16
|
-
function
|
|
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
|
|
25
|
+
function x() {
|
|
25
26
|
try {
|
|
26
27
|
return i();
|
|
27
28
|
} catch {
|
|
28
29
|
return null;
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
|
-
function
|
|
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
|
|
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
|
|
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
|
|
59
|
-
let t =
|
|
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
|
|
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
|
|
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() ??
|
|
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(
|
|
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
|
|
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 =
|
|
135
|
-
if (d &&
|
|
142
|
+
let d = x(), m = E(), h = i || D() || A();
|
|
143
|
+
if (d && m === u && f(d, {
|
|
136
144
|
workspaceId: u,
|
|
137
|
-
organizationId:
|
|
145
|
+
organizationId: h,
|
|
138
146
|
projectId: a
|
|
139
147
|
}) && (!l || l.accepts(d, u))) return d;
|
|
140
|
-
let
|
|
141
|
-
|
|
148
|
+
let g = M(), _ = JSON.stringify([
|
|
149
|
+
h ?? "",
|
|
142
150
|
u,
|
|
143
151
|
a ?? "",
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
...
|
|
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
|
-
|
|
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
|
-
|
|
184
|
+
y.delete(_);
|
|
173
185
|
});
|
|
174
|
-
return
|
|
186
|
+
return y.set(_, S), N(S, s, null);
|
|
175
187
|
}
|
|
176
|
-
function
|
|
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
|
|
192
|
+
function I(e) {
|
|
181
193
|
return e.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
182
194
|
}
|
|
183
|
-
function
|
|
184
|
-
if (e?.aborted) throw
|
|
195
|
+
function L(e) {
|
|
196
|
+
if (e?.aborted) throw I(e);
|
|
185
197
|
}
|
|
186
|
-
function
|
|
198
|
+
function R() {
|
|
187
199
|
return globalThis;
|
|
188
200
|
}
|
|
189
|
-
function
|
|
190
|
-
let e =
|
|
201
|
+
function z() {
|
|
202
|
+
let e = R();
|
|
191
203
|
return e.__graphqlFetchQueues ||= /* @__PURE__ */ new Map(), e.__graphqlFetchQueues;
|
|
192
204
|
}
|
|
193
|
-
function
|
|
194
|
-
let e =
|
|
205
|
+
function B() {
|
|
206
|
+
let e = R();
|
|
195
207
|
return e.__graphqlFetchResponseCache ||= /* @__PURE__ */ new Map(), e.__graphqlFetchResponseCache;
|
|
196
208
|
}
|
|
197
|
-
function
|
|
198
|
-
let e =
|
|
209
|
+
function V() {
|
|
210
|
+
let e = R();
|
|
199
211
|
return e.__graphqlFetchPendingResponses ||= /* @__PURE__ */ new Map(), e.__graphqlFetchPendingResponses;
|
|
200
212
|
}
|
|
201
|
-
var
|
|
202
|
-
function
|
|
203
|
-
return !!(e && typeof e == "object" && e[
|
|
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
|
|
206
|
-
|
|
217
|
+
function ie() {
|
|
218
|
+
B().clear(), V().clear(), n();
|
|
207
219
|
}
|
|
208
|
-
var
|
|
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
|
|
225
|
+
function le() {
|
|
214
226
|
if (typeof performance > "u") return U;
|
|
215
|
-
let e =
|
|
216
|
-
return e.__graphqlFetchBurstStartedAt ??= performance.now(), performance.now() - e.__graphqlFetchBurstStartedAt <
|
|
227
|
+
let e = R();
|
|
228
|
+
return e.__graphqlFetchBurstStartedAt ??= performance.now(), performance.now() - e.__graphqlFetchBurstStartedAt < se ? oe : U;
|
|
217
229
|
}
|
|
218
|
-
function
|
|
230
|
+
function ue(e) {
|
|
219
231
|
return typeof e.query == "string" ? /^\s*query\b/.test(e.query) : !1;
|
|
220
232
|
}
|
|
221
|
-
function
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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) &&
|
|
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) &&
|
|
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 =
|
|
310
|
-
if (r?.aborted) return Promise.reject(
|
|
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 =
|
|
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[
|
|
328
|
+
return e[H] = !0, e;
|
|
317
329
|
}
|
|
318
330
|
e.delete(u);
|
|
319
331
|
}
|
|
320
|
-
let n =
|
|
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 =
|
|
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 =
|
|
351
|
-
e && t >= 0 && (e.requests.splice(t, 1), e.requests.length === 0 && (e.timer && clearTimeout(e.timer),
|
|
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 && (
|
|
359
|
-
|
|
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 =
|
|
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:
|
|
368
|
-
|
|
369
|
-
let
|
|
370
|
-
if (!
|
|
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" &&
|
|
385
|
+
globalBaseUrl: n === "global" && y ? y : t().globalBaseUrl,
|
|
374
386
|
...e.getSharedTokenRefreshRefs()
|
|
375
|
-
}) ?
|
|
387
|
+
}) ? A = b() : _({
|
|
376
388
|
operationName: a,
|
|
377
389
|
gateway: n
|
|
378
|
-
}),
|
|
390
|
+
}), L(w);
|
|
379
391
|
}
|
|
380
|
-
let
|
|
392
|
+
let j = v ?? T(), M = ne({
|
|
381
393
|
workspaceId: c,
|
|
382
394
|
organizationId: u,
|
|
383
|
-
tenantId:
|
|
384
|
-
projectId:
|
|
385
|
-
}),
|
|
395
|
+
tenantId: p,
|
|
396
|
+
projectId: g
|
|
397
|
+
}), N = {
|
|
386
398
|
"Content-Type": "application/json",
|
|
387
399
|
[G]: "true"
|
|
388
400
|
};
|
|
389
|
-
|
|
390
|
-
let
|
|
391
|
-
if (
|
|
392
|
-
let e = typeof s == "string" ? s :
|
|
393
|
-
e && !f(e,
|
|
394
|
-
url:
|
|
395
|
-
authToken:
|
|
396
|
-
workspaceId:
|
|
397
|
-
organizationId:
|
|
398
|
-
projectId:
|
|
399
|
-
locale:
|
|
400
|
-
signal:
|
|
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
|
-
}),
|
|
414
|
+
}), L(w)), e && (N["X-Workspace-Authorization"] = `Bearer ${e}`);
|
|
403
415
|
}
|
|
404
416
|
if (n === "workspace" || c) {
|
|
405
|
-
let e =
|
|
406
|
-
e && (
|
|
417
|
+
let e = M.workspaceId;
|
|
418
|
+
e && (N["x-workspace-id"] = e);
|
|
407
419
|
}
|
|
408
420
|
if (n === "global" || u) {
|
|
409
|
-
let e =
|
|
410
|
-
e && (
|
|
421
|
+
let e = M.organizationId;
|
|
422
|
+
e && (N["x-org-id"] = e);
|
|
411
423
|
}
|
|
412
|
-
if (
|
|
413
|
-
let e = typeof
|
|
414
|
-
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 =
|
|
418
|
-
e && (
|
|
428
|
+
if (n === "workspace" || g) {
|
|
429
|
+
let e = M.projectId;
|
|
430
|
+
e && (N["x-project-id"] = e);
|
|
419
431
|
}
|
|
420
|
-
let
|
|
421
|
-
|
|
422
|
-
let
|
|
423
|
-
i && (
|
|
424
|
-
let
|
|
425
|
-
if (e.skipAuthRetry || !
|
|
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" &&
|
|
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
|
-
}) : (
|
|
447
|
+
}) : (_({
|
|
436
448
|
operationName: a,
|
|
437
449
|
gateway: n
|
|
438
450
|
}), null) : null;
|
|
439
451
|
};
|
|
440
452
|
try {
|
|
441
|
-
let e = Z(
|
|
442
|
-
if (
|
|
443
|
-
let e = await
|
|
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
|
-
|
|
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
|
|
463
|
+
let e = await B(t);
|
|
452
464
|
if (e) return e;
|
|
453
465
|
}
|
|
454
|
-
throw
|
|
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 {
|
|
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;
|