@trackunit/react-core-contexts 2.1.32 → 2.1.33

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 CHANGED
@@ -103,14 +103,28 @@ const createErrorLink = ({ errorHandler, getToken, }) => {
103
103
  });
104
104
  };
105
105
 
106
+ const DEFAULT_AUTH_GRACE_MS = 30000;
106
107
  /**
107
108
  * Wraps an SSE subscription link and provides the same error monitoring as the
108
109
  * HTTP error link — capturing GraphQL errors, traceIds, FORCE_RELOAD_BROWSER,
109
110
  * and UNAUTHENTICATED codes — for long-lived subscription Observables.
111
+ *
112
+ * UNAUTHENTICATED errors are deferred by a grace period (default 30s) to allow
113
+ * Okta's autoRenew to refresh the token. If a successful response arrives
114
+ * within the window the capture is cancelled; if the timer fires, a single
115
+ * captureException is sent — an actionable signal that token refresh failed.
110
116
  */
111
- const createSubscriptionErrorLink = ({ errorHandler, getToken, }) => {
117
+ const createSubscriptionErrorLink = ({ errorHandler, getToken, graceMs = DEFAULT_AUTH_GRACE_MS, }) => {
112
118
  return new client.ApolloLink((operation, forward) => {
113
119
  return new client.Observable(observer => {
120
+ let authGraceTimer = null;
121
+ let authErrorReported = false;
122
+ const clearGraceTimer = () => {
123
+ if (authGraceTimer !== null) {
124
+ clearTimeout(authGraceTimer);
125
+ authGraceTimer = null;
126
+ }
127
+ };
114
128
  const subscription = forward(operation).subscribe({
115
129
  next: response => {
116
130
  const { errors } = response;
@@ -137,15 +151,23 @@ const createSubscriptionErrorLink = ({ errorHandler, getToken, }) => {
137
151
  const invalidToken = errors.some(x => x.extensions?.code === "UNAUTHENTICATED" ||
138
152
  x.message.includes("Invalid token specified") ||
139
153
  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"] });
154
+ if (invalidToken && getToken() && authGraceTimer === null && !authErrorReported) {
155
+ authGraceTimer = setTimeout(() => {
156
+ authGraceTimer = null;
157
+ authErrorReported = true;
158
+ errorHandler.captureException(new Error(JSON.stringify({
159
+ category: "GraphQL",
160
+ info: "GraphQL Subscription Error - invalidToken",
161
+ level: "warning",
162
+ data: { log: JSON.stringify(errors) },
163
+ })), { level: "warning", fingerprint: ["GraphQL Subscription Error - invalidToken"] });
164
+ }, graceMs);
147
165
  }
148
166
  }
167
+ else {
168
+ clearGraceTimer();
169
+ authErrorReported = false;
170
+ }
149
171
  observer.next(response);
150
172
  },
151
173
  error: err => {
@@ -168,7 +190,10 @@ const createSubscriptionErrorLink = ({ errorHandler, getToken, }) => {
168
190
  },
169
191
  complete: () => observer.complete(),
170
192
  });
171
- return () => subscription.unsubscribe();
193
+ return () => {
194
+ clearGraceTimer();
195
+ subscription.unsubscribe();
196
+ };
172
197
  });
173
198
  });
174
199
  };
package/index.esm.js CHANGED
@@ -101,14 +101,28 @@ const createErrorLink = ({ errorHandler, getToken, }) => {
101
101
  });
102
102
  };
103
103
 
104
+ const DEFAULT_AUTH_GRACE_MS = 30000;
104
105
  /**
105
106
  * Wraps an SSE subscription link and provides the same error monitoring as the
106
107
  * HTTP error link — capturing GraphQL errors, traceIds, FORCE_RELOAD_BROWSER,
107
108
  * and UNAUTHENTICATED codes — for long-lived subscription Observables.
109
+ *
110
+ * UNAUTHENTICATED errors are deferred by a grace period (default 30s) to allow
111
+ * Okta's autoRenew to refresh the token. If a successful response arrives
112
+ * within the window the capture is cancelled; if the timer fires, a single
113
+ * captureException is sent — an actionable signal that token refresh failed.
108
114
  */
109
- const createSubscriptionErrorLink = ({ errorHandler, getToken, }) => {
115
+ const createSubscriptionErrorLink = ({ errorHandler, getToken, graceMs = DEFAULT_AUTH_GRACE_MS, }) => {
110
116
  return new ApolloLink((operation, forward) => {
111
117
  return new Observable(observer => {
118
+ let authGraceTimer = null;
119
+ let authErrorReported = false;
120
+ const clearGraceTimer = () => {
121
+ if (authGraceTimer !== null) {
122
+ clearTimeout(authGraceTimer);
123
+ authGraceTimer = null;
124
+ }
125
+ };
112
126
  const subscription = forward(operation).subscribe({
113
127
  next: response => {
114
128
  const { errors } = response;
@@ -135,15 +149,23 @@ const createSubscriptionErrorLink = ({ errorHandler, getToken, }) => {
135
149
  const invalidToken = errors.some(x => x.extensions?.code === "UNAUTHENTICATED" ||
136
150
  x.message.includes("Invalid token specified") ||
137
151
  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"] });
152
+ if (invalidToken && getToken() && authGraceTimer === null && !authErrorReported) {
153
+ authGraceTimer = setTimeout(() => {
154
+ authGraceTimer = null;
155
+ authErrorReported = true;
156
+ errorHandler.captureException(new Error(JSON.stringify({
157
+ category: "GraphQL",
158
+ info: "GraphQL Subscription Error - invalidToken",
159
+ level: "warning",
160
+ data: { log: JSON.stringify(errors) },
161
+ })), { level: "warning", fingerprint: ["GraphQL Subscription Error - invalidToken"] });
162
+ }, graceMs);
145
163
  }
146
164
  }
165
+ else {
166
+ clearGraceTimer();
167
+ authErrorReported = false;
168
+ }
147
169
  observer.next(response);
148
170
  },
149
171
  error: err => {
@@ -166,7 +188,10 @@ const createSubscriptionErrorLink = ({ errorHandler, getToken, }) => {
166
188
  },
167
189
  complete: () => observer.complete(),
168
190
  });
169
- return () => subscription.unsubscribe();
191
+ return () => {
192
+ clearGraceTimer();
193
+ subscription.unsubscribe();
194
+ };
170
195
  });
171
196
  });
172
197
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-core-contexts",
3
- "version": "2.1.32",
3
+ "version": "2.1.33",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -4,8 +4,14 @@ import { ErrorHandlingContextValue } from "@trackunit/iris-app-runtime-core-api"
4
4
  * Wraps an SSE subscription link and provides the same error monitoring as the
5
5
  * HTTP error link — capturing GraphQL errors, traceIds, FORCE_RELOAD_BROWSER,
6
6
  * and UNAUTHENTICATED codes — for long-lived subscription Observables.
7
+ *
8
+ * UNAUTHENTICATED errors are deferred by a grace period (default 30s) to allow
9
+ * Okta's autoRenew to refresh the token. If a successful response arrives
10
+ * within the window the capture is cancelled; if the timer fires, a single
11
+ * captureException is sent — an actionable signal that token refresh failed.
7
12
  */
8
- export declare const createSubscriptionErrorLink: ({ errorHandler, getToken, }: {
13
+ export declare const createSubscriptionErrorLink: ({ errorHandler, getToken, graceMs, }: {
9
14
  errorHandler: ErrorHandlingContextValue;
10
15
  getToken: () => string | undefined;
16
+ graceMs?: number;
11
17
  }) => ApolloLink;