@trackunit/react-core-contexts 1.30.6 → 1.30.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/index.cjs.js +101 -17
- package/index.esm.js +104 -20
- package/package.json +8 -8
- package/src/createApolloClient.d.ts +19 -0
- package/src/errorLink/errorLink.d.ts +2 -2
- package/src/errorLink/subscriptionErrorLink.d.ts +11 -0
package/index.cjs.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var client = require('@apollo/client');
|
|
5
|
+
var reactCoreHooks = require('@trackunit/react-core-hooks');
|
|
6
|
+
var react = require('react');
|
|
5
7
|
var context = require('@apollo/client/link/context');
|
|
6
8
|
var removeTypename = require('@apollo/client/link/remove-typename');
|
|
7
9
|
var utilities = require('@apollo/client/utilities');
|
|
8
|
-
var reactCoreHooks = require('@trackunit/react-core-hooks');
|
|
9
10
|
var graphql = require('graphql');
|
|
10
11
|
var graphqlSse = require('graphql-sse');
|
|
11
|
-
var react = require('react');
|
|
12
12
|
var error = require('@apollo/client/link/error');
|
|
13
13
|
var irisAppRuntimeCore = require('@trackunit/iris-app-runtime-core');
|
|
14
14
|
var reactCoreContextsApi = require('@trackunit/react-core-contexts-api');
|
|
@@ -19,7 +19,7 @@ var irisAppRuntimeCoreApi = require('@trackunit/iris-app-runtime-core-api');
|
|
|
19
19
|
/**
|
|
20
20
|
* This error link is used to capture error information, i. e. traceId, graphQL errors, network errors, etc.
|
|
21
21
|
*/
|
|
22
|
-
const createErrorLink = ({ errorHandler,
|
|
22
|
+
const createErrorLink = ({ errorHandler, getToken, }) => {
|
|
23
23
|
return error.onError(({ graphQLErrors, networkError, operation, forward }) => {
|
|
24
24
|
if (networkError) {
|
|
25
25
|
// We skip the error logging if the error is an AbortError
|
|
@@ -67,7 +67,7 @@ const createErrorLink = ({ errorHandler, token, }) => {
|
|
|
67
67
|
x.message.includes("Invalid token specified") ||
|
|
68
68
|
x.message.includes("Access denied! You need to be authorized to perform this action!"));
|
|
69
69
|
});
|
|
70
|
-
if (invalidToken &&
|
|
70
|
+
if (invalidToken && getToken()) {
|
|
71
71
|
errorHandler.captureException(new Error(JSON.stringify({
|
|
72
72
|
category: "GraphQL",
|
|
73
73
|
info: "GraphQL Error - invalidToken",
|
|
@@ -103,6 +103,76 @@ const createErrorLink = ({ errorHandler, token, }) => {
|
|
|
103
103
|
});
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Wraps an SSE subscription link and provides the same error monitoring as the
|
|
108
|
+
* HTTP error link — capturing GraphQL errors, traceIds, FORCE_RELOAD_BROWSER,
|
|
109
|
+
* and UNAUTHENTICATED codes — for long-lived subscription Observables.
|
|
110
|
+
*/
|
|
111
|
+
const createSubscriptionErrorLink = ({ errorHandler, getToken, }) => {
|
|
112
|
+
return new client.ApolloLink((operation, forward) => {
|
|
113
|
+
return new client.Observable(observer => {
|
|
114
|
+
const subscription = forward(operation).subscribe({
|
|
115
|
+
next: response => {
|
|
116
|
+
const { errors } = response;
|
|
117
|
+
if (errors) {
|
|
118
|
+
const code = errors[0]?.extensions?.code;
|
|
119
|
+
if (code === "FORCE_RELOAD_BROWSER") {
|
|
120
|
+
window.location.reload();
|
|
121
|
+
}
|
|
122
|
+
const traceIds = [];
|
|
123
|
+
errors.forEach(error => {
|
|
124
|
+
if ("extensions" in error && error.extensions && typeof error.extensions.traceId === "string") {
|
|
125
|
+
traceIds.push(error.extensions.traceId);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
if (traceIds.length) {
|
|
129
|
+
errorHandler.setTag("traceIds", traceIds.join(", "));
|
|
130
|
+
}
|
|
131
|
+
errorHandler.addBreadcrumb({
|
|
132
|
+
category: "GraphQL",
|
|
133
|
+
message: "GraphQL Subscription Error",
|
|
134
|
+
level: "error",
|
|
135
|
+
data: { log: JSON.stringify(errors) },
|
|
136
|
+
});
|
|
137
|
+
const invalidToken = errors.some(x => x.extensions?.code === "UNAUTHENTICATED" ||
|
|
138
|
+
x.message.includes("Invalid token specified") ||
|
|
139
|
+
x.message.includes("Access denied! You need to be authorized to perform this action!"));
|
|
140
|
+
if (invalidToken && getToken()) {
|
|
141
|
+
errorHandler.captureException(new Error(JSON.stringify({
|
|
142
|
+
category: "GraphQL",
|
|
143
|
+
info: "GraphQL Subscription Error - invalidToken",
|
|
144
|
+
level: "warning",
|
|
145
|
+
data: { log: JSON.stringify(errors) },
|
|
146
|
+
})), { level: "warning", fingerprint: ["GraphQL Subscription Error - invalidToken"] });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
observer.next(response);
|
|
150
|
+
},
|
|
151
|
+
error: err => {
|
|
152
|
+
// AbortError is expected when a subscription is cancelled (component
|
|
153
|
+
// unmount or navigation) — graphql-sse calls control.abort() which
|
|
154
|
+
// fires this error asynchronously. Swallow it to prevent an
|
|
155
|
+
// unhandled rejection, mirroring the guard in createErrorLink.
|
|
156
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
// eslint-disable-next-line no-console
|
|
160
|
+
console.error(err);
|
|
161
|
+
errorHandler.addBreadcrumb({
|
|
162
|
+
category: "GraphQL",
|
|
163
|
+
message: "GraphQL Subscription Network Error",
|
|
164
|
+
level: "error",
|
|
165
|
+
data: { log: String(err) },
|
|
166
|
+
});
|
|
167
|
+
observer.error(err);
|
|
168
|
+
},
|
|
169
|
+
complete: () => observer.complete(),
|
|
170
|
+
});
|
|
171
|
+
return () => subscription.unsubscribe();
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
|
|
106
176
|
// Use a widened `string` key so TypeScript picks the `unknown` overload of
|
|
107
177
|
// `Reflect.get` instead of the typed one — otherwise reads like `module`
|
|
108
178
|
// resolve to `NodeModule` from @types/node.
|
|
@@ -170,11 +240,12 @@ const isInternalGqlContext = () => {
|
|
|
170
240
|
return Reflect.get(globalThis, "gql") === "internal";
|
|
171
241
|
};
|
|
172
242
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
243
|
+
/**
|
|
244
|
+
* @internal
|
|
245
|
+
*/
|
|
246
|
+
const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, isDev, tracingHeaders: initialTracingHeaders, firstToken, errorHandler, }) => {
|
|
247
|
+
let token = firstToken;
|
|
248
|
+
let tracingHeaders = initialTracingHeaders;
|
|
178
249
|
const publicGraphQLLink = client.createHttpLink({
|
|
179
250
|
uri: request => graphqlPublicUrl + "/" + request.operationName,
|
|
180
251
|
});
|
|
@@ -184,7 +255,7 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
184
255
|
const reportGraphQLLink = client.createHttpLink({
|
|
185
256
|
uri: request => graphqlReportUrl + "/" + request.operationName,
|
|
186
257
|
});
|
|
187
|
-
const authLink = context.setContext(
|
|
258
|
+
const authLink = context.setContext((_, { headers: existingHeaders }) => {
|
|
188
259
|
return {
|
|
189
260
|
headers: {
|
|
190
261
|
...existingHeaders,
|
|
@@ -192,7 +263,7 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
192
263
|
},
|
|
193
264
|
};
|
|
194
265
|
});
|
|
195
|
-
const errorLink = createErrorLink({ errorHandler, token });
|
|
266
|
+
const errorLink = createErrorLink({ errorHandler, getToken: () => token });
|
|
196
267
|
const defaultOptions = {
|
|
197
268
|
watchQuery: {
|
|
198
269
|
fetchPolicy: "no-cache",
|
|
@@ -209,10 +280,12 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
209
280
|
};
|
|
210
281
|
const removeTypenameLink = removeTypename.removeTypenameFromVariables();
|
|
211
282
|
class SSELink extends client.ApolloLink {
|
|
283
|
+
/** @inheritdoc */
|
|
212
284
|
constructor(options) {
|
|
213
285
|
super();
|
|
214
286
|
this.client = graphqlSse.createClient(options);
|
|
215
287
|
}
|
|
288
|
+
/** @inheritdoc */
|
|
216
289
|
request(operation) {
|
|
217
290
|
return new utilities.Observable(sink => {
|
|
218
291
|
return this.client.subscribe({ ...operation, query: graphql.print(operation.query) }, {
|
|
@@ -228,12 +301,13 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
228
301
|
headers: () => generateHeaders(token, tracingHeaders),
|
|
229
302
|
});
|
|
230
303
|
// Split links based on operation type
|
|
304
|
+
const subscriptionErrorLink = createSubscriptionErrorLink({ errorHandler, getToken: () => token });
|
|
231
305
|
const splitLink = client.from([
|
|
232
306
|
authLink,
|
|
233
307
|
client.split(({ query }) => {
|
|
234
308
|
const definition = utilities.getMainDefinition(query);
|
|
235
309
|
return definition.kind === "OperationDefinition" && definition.operation === "subscription";
|
|
236
|
-
}, sseLink, client.from([
|
|
310
|
+
}, client.from([subscriptionErrorLink, sseLink]), client.from([
|
|
237
311
|
errorLink,
|
|
238
312
|
removeTypenameLink,
|
|
239
313
|
client.split(operation => operation.getContext().clientName === "report", reportGraphQLLink, client.split(() => isInternalGqlContext(), internalGraphQLLink, publicGraphQLLink)),
|
|
@@ -261,8 +335,12 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
261
335
|
getToken: () => {
|
|
262
336
|
return token;
|
|
263
337
|
},
|
|
338
|
+
setTracingHeaders: (newHeaders) => {
|
|
339
|
+
tracingHeaders = newHeaders;
|
|
340
|
+
},
|
|
264
341
|
};
|
|
265
342
|
};
|
|
343
|
+
|
|
266
344
|
const useApolloClient = () => {
|
|
267
345
|
const { graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, environment, tracingHeaders } = reactCoreHooks.useEnvironment();
|
|
268
346
|
const { token: currentToken } = reactCoreHooks.useToken();
|
|
@@ -281,11 +359,17 @@ const useApolloClient = () => {
|
|
|
281
359
|
errorHandler,
|
|
282
360
|
});
|
|
283
361
|
});
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
362
|
+
// Synchronously propagate the token into the Apollo client closure during render
|
|
363
|
+
// so that child components' effects (useLayoutEffect / useEffect) always see the
|
|
364
|
+
// current token when they fire their first request in the same commit.
|
|
365
|
+
// React runs child effects before parent effects, so a parent useEffect here would
|
|
366
|
+
// be too late — child queries would go out with the previous token for one commit.
|
|
367
|
+
if (client.getToken() !== currentToken) {
|
|
368
|
+
client.setToken(currentToken);
|
|
369
|
+
}
|
|
370
|
+
react.useEffect(() => {
|
|
371
|
+
client.setTracingHeaders(tracingHeaders);
|
|
372
|
+
}, [client, tracingHeaders]);
|
|
289
373
|
return client;
|
|
290
374
|
};
|
|
291
375
|
/**
|
package/index.esm.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
2
|
+
import { ApolloLink, Observable, createHttpLink, from, split, ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
|
|
3
|
+
import { useEnvironment, useToken, useErrorHandler } from '@trackunit/react-core-hooks';
|
|
4
|
+
import { useState, useEffect, useMemo, useCallback, useReducer, Suspense } from 'react';
|
|
3
5
|
import { setContext } from '@apollo/client/link/context';
|
|
4
6
|
import { removeTypenameFromVariables } from '@apollo/client/link/remove-typename';
|
|
5
|
-
import { getMainDefinition, Observable } from '@apollo/client/utilities';
|
|
6
|
-
import { useEnvironment, useToken, useErrorHandler } from '@trackunit/react-core-hooks';
|
|
7
|
+
import { getMainDefinition, Observable as Observable$1 } from '@apollo/client/utilities';
|
|
7
8
|
import { print } from 'graphql';
|
|
8
9
|
import { createClient } from 'graphql-sse';
|
|
9
|
-
import { useState, useMemo, useEffect, useCallback, useReducer, Suspense } from 'react';
|
|
10
10
|
import { onError } from '@apollo/client/link/error';
|
|
11
11
|
import { ToastRuntime, AnalyticsRuntime, registerHostChangeHandler, AssetSortingRuntime, ConfirmationDialogRuntime, EnvironmentRuntime, ExportDataRuntime, AssetsFilterBarRuntime, CustomersFilterBarRuntime, SitesFilterBarRuntime, GeolocationRuntime, ModalDialogRuntime, NavigationRuntime, OemBrandingRuntime, ThemeCssRuntime, TimeRangeRuntime, TokenRuntime, CurrentUserRuntime, CurrentUserPreferenceRuntime, UserSubscriptionRuntime, WidgetConfigRuntime } from '@trackunit/iris-app-runtime-core';
|
|
12
12
|
import { ToastProvider, AnalyticsContextProvider, AssetSortingProvider, ConfirmationDialogProvider, EnvironmentContextProvider, ErrorHandlingContextProvider, ExportDataContext, FilterBarProvider, GeolocationProvider, ModalDialogContextProvider, NavigationContextProvider, OemBrandingContextProvider, TimeRangeProvider, TokenProvider, CurrentUserProvider, CurrentUserPreferenceProvider, UserSubscriptionProvider, WidgetConfigProvider } from '@trackunit/react-core-contexts-api';
|
|
@@ -17,7 +17,7 @@ import { Channels, SortOrder, AssetSortByProperty } from '@trackunit/iris-app-ru
|
|
|
17
17
|
/**
|
|
18
18
|
* This error link is used to capture error information, i. e. traceId, graphQL errors, network errors, etc.
|
|
19
19
|
*/
|
|
20
|
-
const createErrorLink = ({ errorHandler,
|
|
20
|
+
const createErrorLink = ({ errorHandler, getToken, }) => {
|
|
21
21
|
return onError(({ graphQLErrors, networkError, operation, forward }) => {
|
|
22
22
|
if (networkError) {
|
|
23
23
|
// We skip the error logging if the error is an AbortError
|
|
@@ -65,7 +65,7 @@ const createErrorLink = ({ errorHandler, token, }) => {
|
|
|
65
65
|
x.message.includes("Invalid token specified") ||
|
|
66
66
|
x.message.includes("Access denied! You need to be authorized to perform this action!"));
|
|
67
67
|
});
|
|
68
|
-
if (invalidToken &&
|
|
68
|
+
if (invalidToken && getToken()) {
|
|
69
69
|
errorHandler.captureException(new Error(JSON.stringify({
|
|
70
70
|
category: "GraphQL",
|
|
71
71
|
info: "GraphQL Error - invalidToken",
|
|
@@ -101,6 +101,76 @@ const createErrorLink = ({ errorHandler, token, }) => {
|
|
|
101
101
|
});
|
|
102
102
|
};
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Wraps an SSE subscription link and provides the same error monitoring as the
|
|
106
|
+
* HTTP error link — capturing GraphQL errors, traceIds, FORCE_RELOAD_BROWSER,
|
|
107
|
+
* and UNAUTHENTICATED codes — for long-lived subscription Observables.
|
|
108
|
+
*/
|
|
109
|
+
const createSubscriptionErrorLink = ({ errorHandler, getToken, }) => {
|
|
110
|
+
return new ApolloLink((operation, forward) => {
|
|
111
|
+
return new Observable(observer => {
|
|
112
|
+
const subscription = forward(operation).subscribe({
|
|
113
|
+
next: response => {
|
|
114
|
+
const { errors } = response;
|
|
115
|
+
if (errors) {
|
|
116
|
+
const code = errors[0]?.extensions?.code;
|
|
117
|
+
if (code === "FORCE_RELOAD_BROWSER") {
|
|
118
|
+
window.location.reload();
|
|
119
|
+
}
|
|
120
|
+
const traceIds = [];
|
|
121
|
+
errors.forEach(error => {
|
|
122
|
+
if ("extensions" in error && error.extensions && typeof error.extensions.traceId === "string") {
|
|
123
|
+
traceIds.push(error.extensions.traceId);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
if (traceIds.length) {
|
|
127
|
+
errorHandler.setTag("traceIds", traceIds.join(", "));
|
|
128
|
+
}
|
|
129
|
+
errorHandler.addBreadcrumb({
|
|
130
|
+
category: "GraphQL",
|
|
131
|
+
message: "GraphQL Subscription Error",
|
|
132
|
+
level: "error",
|
|
133
|
+
data: { log: JSON.stringify(errors) },
|
|
134
|
+
});
|
|
135
|
+
const invalidToken = errors.some(x => x.extensions?.code === "UNAUTHENTICATED" ||
|
|
136
|
+
x.message.includes("Invalid token specified") ||
|
|
137
|
+
x.message.includes("Access denied! You need to be authorized to perform this action!"));
|
|
138
|
+
if (invalidToken && getToken()) {
|
|
139
|
+
errorHandler.captureException(new Error(JSON.stringify({
|
|
140
|
+
category: "GraphQL",
|
|
141
|
+
info: "GraphQL Subscription Error - invalidToken",
|
|
142
|
+
level: "warning",
|
|
143
|
+
data: { log: JSON.stringify(errors) },
|
|
144
|
+
})), { level: "warning", fingerprint: ["GraphQL Subscription Error - invalidToken"] });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
observer.next(response);
|
|
148
|
+
},
|
|
149
|
+
error: err => {
|
|
150
|
+
// AbortError is expected when a subscription is cancelled (component
|
|
151
|
+
// unmount or navigation) — graphql-sse calls control.abort() which
|
|
152
|
+
// fires this error asynchronously. Swallow it to prevent an
|
|
153
|
+
// unhandled rejection, mirroring the guard in createErrorLink.
|
|
154
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
// eslint-disable-next-line no-console
|
|
158
|
+
console.error(err);
|
|
159
|
+
errorHandler.addBreadcrumb({
|
|
160
|
+
category: "GraphQL",
|
|
161
|
+
message: "GraphQL Subscription Network Error",
|
|
162
|
+
level: "error",
|
|
163
|
+
data: { log: String(err) },
|
|
164
|
+
});
|
|
165
|
+
observer.error(err);
|
|
166
|
+
},
|
|
167
|
+
complete: () => observer.complete(),
|
|
168
|
+
});
|
|
169
|
+
return () => subscription.unsubscribe();
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
|
|
104
174
|
// Use a widened `string` key so TypeScript picks the `unknown` overload of
|
|
105
175
|
// `Reflect.get` instead of the typed one — otherwise reads like `module`
|
|
106
176
|
// resolve to `NodeModule` from @types/node.
|
|
@@ -168,11 +238,12 @@ const isInternalGqlContext = () => {
|
|
|
168
238
|
return Reflect.get(globalThis, "gql") === "internal";
|
|
169
239
|
};
|
|
170
240
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
241
|
+
/**
|
|
242
|
+
* @internal
|
|
243
|
+
*/
|
|
244
|
+
const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, isDev, tracingHeaders: initialTracingHeaders, firstToken, errorHandler, }) => {
|
|
245
|
+
let token = firstToken;
|
|
246
|
+
let tracingHeaders = initialTracingHeaders;
|
|
176
247
|
const publicGraphQLLink = createHttpLink({
|
|
177
248
|
uri: request => graphqlPublicUrl + "/" + request.operationName,
|
|
178
249
|
});
|
|
@@ -182,7 +253,7 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
182
253
|
const reportGraphQLLink = createHttpLink({
|
|
183
254
|
uri: request => graphqlReportUrl + "/" + request.operationName,
|
|
184
255
|
});
|
|
185
|
-
const authLink = setContext(
|
|
256
|
+
const authLink = setContext((_, { headers: existingHeaders }) => {
|
|
186
257
|
return {
|
|
187
258
|
headers: {
|
|
188
259
|
...existingHeaders,
|
|
@@ -190,7 +261,7 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
190
261
|
},
|
|
191
262
|
};
|
|
192
263
|
});
|
|
193
|
-
const errorLink = createErrorLink({ errorHandler, token });
|
|
264
|
+
const errorLink = createErrorLink({ errorHandler, getToken: () => token });
|
|
194
265
|
const defaultOptions = {
|
|
195
266
|
watchQuery: {
|
|
196
267
|
fetchPolicy: "no-cache",
|
|
@@ -207,12 +278,14 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
207
278
|
};
|
|
208
279
|
const removeTypenameLink = removeTypenameFromVariables();
|
|
209
280
|
class SSELink extends ApolloLink {
|
|
281
|
+
/** @inheritdoc */
|
|
210
282
|
constructor(options) {
|
|
211
283
|
super();
|
|
212
284
|
this.client = createClient(options);
|
|
213
285
|
}
|
|
286
|
+
/** @inheritdoc */
|
|
214
287
|
request(operation) {
|
|
215
|
-
return new Observable(sink => {
|
|
288
|
+
return new Observable$1(sink => {
|
|
216
289
|
return this.client.subscribe({ ...operation, query: print(operation.query) }, {
|
|
217
290
|
next: value => sink.next(value),
|
|
218
291
|
complete: sink.complete.bind(sink),
|
|
@@ -226,12 +299,13 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
226
299
|
headers: () => generateHeaders(token, tracingHeaders),
|
|
227
300
|
});
|
|
228
301
|
// Split links based on operation type
|
|
302
|
+
const subscriptionErrorLink = createSubscriptionErrorLink({ errorHandler, getToken: () => token });
|
|
229
303
|
const splitLink = from([
|
|
230
304
|
authLink,
|
|
231
305
|
split(({ query }) => {
|
|
232
306
|
const definition = getMainDefinition(query);
|
|
233
307
|
return definition.kind === "OperationDefinition" && definition.operation === "subscription";
|
|
234
|
-
}, sseLink, from([
|
|
308
|
+
}, from([subscriptionErrorLink, sseLink]), from([
|
|
235
309
|
errorLink,
|
|
236
310
|
removeTypenameLink,
|
|
237
311
|
split(operation => operation.getContext().clientName === "report", reportGraphQLLink, split(() => isInternalGqlContext(), internalGraphQLLink, publicGraphQLLink)),
|
|
@@ -259,8 +333,12 @@ const createApolloClient = ({ graphqlPublicUrl, graphqlInternalUrl, graphqlRepor
|
|
|
259
333
|
getToken: () => {
|
|
260
334
|
return token;
|
|
261
335
|
},
|
|
336
|
+
setTracingHeaders: (newHeaders) => {
|
|
337
|
+
tracingHeaders = newHeaders;
|
|
338
|
+
},
|
|
262
339
|
};
|
|
263
340
|
};
|
|
341
|
+
|
|
264
342
|
const useApolloClient = () => {
|
|
265
343
|
const { graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, environment, tracingHeaders } = useEnvironment();
|
|
266
344
|
const { token: currentToken } = useToken();
|
|
@@ -279,11 +357,17 @@ const useApolloClient = () => {
|
|
|
279
357
|
errorHandler,
|
|
280
358
|
});
|
|
281
359
|
});
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
360
|
+
// Synchronously propagate the token into the Apollo client closure during render
|
|
361
|
+
// so that child components' effects (useLayoutEffect / useEffect) always see the
|
|
362
|
+
// current token when they fire their first request in the same commit.
|
|
363
|
+
// React runs child effects before parent effects, so a parent useEffect here would
|
|
364
|
+
// be too late — child queries would go out with the previous token for one commit.
|
|
365
|
+
if (client.getToken() !== currentToken) {
|
|
366
|
+
client.setToken(currentToken);
|
|
367
|
+
}
|
|
368
|
+
useEffect(() => {
|
|
369
|
+
client.setTracingHeaders(tracingHeaders);
|
|
370
|
+
}, [client, tracingHeaders]);
|
|
287
371
|
return client;
|
|
288
372
|
};
|
|
289
373
|
/**
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-core-contexts",
|
|
3
|
-
"version": "1.30.
|
|
3
|
+
"version": "1.30.9",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=24.x"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@trackunit/iris-app-api": "1.20.
|
|
11
|
-
"@trackunit/iris-app-runtime-core-api": "1.16.
|
|
12
|
-
"@trackunit/react-core-hooks": "1.17.
|
|
13
|
-
"@trackunit/i18n-library-translation": "1.22.
|
|
14
|
-
"@trackunit/react-components": "1.26.
|
|
15
|
-
"@trackunit/iris-app-runtime-core": "1.17.
|
|
10
|
+
"@trackunit/iris-app-api": "1.20.10",
|
|
11
|
+
"@trackunit/iris-app-runtime-core-api": "1.16.9",
|
|
12
|
+
"@trackunit/react-core-hooks": "1.17.13",
|
|
13
|
+
"@trackunit/i18n-library-translation": "1.22.4",
|
|
14
|
+
"@trackunit/react-components": "1.26.8",
|
|
15
|
+
"@trackunit/iris-app-runtime-core": "1.17.9",
|
|
16
16
|
"graphql-sse": "^2.5.4",
|
|
17
|
-
"@trackunit/react-core-contexts-api": "1.17.
|
|
17
|
+
"@trackunit/react-core-contexts-api": "1.17.9"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"@apollo/client": "^3.13.8",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ApolloClient } from "@apollo/client";
|
|
2
|
+
import { ErrorHandlingContextValue, TracingHeaders } from "@trackunit/iris-app-runtime-core-api";
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export declare const createApolloClient: ({ graphqlPublicUrl, graphqlInternalUrl, graphqlReportUrl, isDev, tracingHeaders: initialTracingHeaders, firstToken, errorHandler, }: {
|
|
7
|
+
graphqlPublicUrl: string;
|
|
8
|
+
graphqlInternalUrl: string;
|
|
9
|
+
graphqlReportUrl: string;
|
|
10
|
+
tracingHeaders: TracingHeaders;
|
|
11
|
+
isDev: boolean;
|
|
12
|
+
firstToken?: string;
|
|
13
|
+
errorHandler: ErrorHandlingContextValue;
|
|
14
|
+
}) => {
|
|
15
|
+
client: ApolloClient<import("@apollo/client").NormalizedCacheObject>;
|
|
16
|
+
setToken: (newToken: string | undefined) => void;
|
|
17
|
+
getToken: () => string | undefined;
|
|
18
|
+
setTracingHeaders: (newHeaders: TracingHeaders) => void;
|
|
19
|
+
};
|
|
@@ -3,7 +3,7 @@ import { ErrorHandlingContextValue } from "@trackunit/iris-app-runtime-core-api"
|
|
|
3
3
|
/**
|
|
4
4
|
* This error link is used to capture error information, i. e. traceId, graphQL errors, network errors, etc.
|
|
5
5
|
*/
|
|
6
|
-
export declare const createErrorLink: ({ errorHandler,
|
|
6
|
+
export declare const createErrorLink: ({ errorHandler, getToken, }: {
|
|
7
7
|
errorHandler: ErrorHandlingContextValue;
|
|
8
|
-
|
|
8
|
+
getToken: () => string | undefined;
|
|
9
9
|
}) => ApolloLink;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ApolloLink } from "@apollo/client";
|
|
2
|
+
import { ErrorHandlingContextValue } from "@trackunit/iris-app-runtime-core-api";
|
|
3
|
+
/**
|
|
4
|
+
* Wraps an SSE subscription link and provides the same error monitoring as the
|
|
5
|
+
* HTTP error link — capturing GraphQL errors, traceIds, FORCE_RELOAD_BROWSER,
|
|
6
|
+
* and UNAUTHENTICATED codes — for long-lived subscription Observables.
|
|
7
|
+
*/
|
|
8
|
+
export declare const createSubscriptionErrorLink: ({ errorHandler, getToken, }: {
|
|
9
|
+
errorHandler: ErrorHandlingContextValue;
|
|
10
|
+
getToken: () => string | undefined;
|
|
11
|
+
}) => ApolloLink;
|