@nauth-toolkit/client-angular 0.1.57 → 0.1.58

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.
@@ -29,6 +29,37 @@ export class AngularHttpAdapter {
29
29
  constructor(http) {
30
30
  this.http = http;
31
31
  }
32
+ /**
33
+ * Safely parse a JSON response body.
34
+ *
35
+ * Angular's fetch backend (`withFetch()`) will throw a raw `SyntaxError` if
36
+ * `responseType: 'json'` is used and the backend returns HTML (common for
37
+ * proxies, 502 pages, SSR fallbacks, or misrouted requests).
38
+ *
39
+ * To avoid crashing consumer apps, we always request as text and then parse
40
+ * JSON only when the response actually looks like JSON.
41
+ *
42
+ * @param bodyText - Raw response body as text
43
+ * @param contentType - Content-Type header value (if available)
44
+ * @returns Parsed JSON value (unknown)
45
+ * @throws {SyntaxError} When body is non-empty but not valid JSON
46
+ */
47
+ parseJsonBody(bodyText, contentType) {
48
+ const trimmed = bodyText.trim();
49
+ if (!trimmed)
50
+ return null;
51
+ // If it's clearly HTML, never attempt JSON.parse (some proxies mislabel Content-Type).
52
+ if (trimmed.startsWith('<')) {
53
+ return bodyText;
54
+ }
55
+ const looksLikeJson = trimmed.startsWith('{') || trimmed.startsWith('[');
56
+ const isJsonContentType = typeof contentType === 'string' && contentType.toLowerCase().includes('application/json');
57
+ if (!looksLikeJson && !isJsonContentType) {
58
+ // Return raw text when it doesn't look like JSON (e.g., HTML error pages).
59
+ return bodyText;
60
+ }
61
+ return JSON.parse(trimmed);
62
+ }
32
63
  /**
33
64
  * Execute HTTP request using Angular's HttpClient.
34
65
  *
@@ -38,29 +69,40 @@ export class AngularHttpAdapter {
38
69
  */
39
70
  async request(config) {
40
71
  try {
41
- // Use Angular's HttpClient - goes through ALL interceptors
42
- const data = await firstValueFrom(this.http.request(config.method, config.url, {
72
+ // Use Angular's HttpClient - goes through ALL interceptors.
73
+ // IMPORTANT: Use responseType 'text' to avoid raw JSON.parse crashes when
74
+ // the backend returns HTML (seen in some proxy/SSR/misroute setups).
75
+ const res = await firstValueFrom(this.http.request(config.method, config.url, {
43
76
  body: config.body,
44
77
  headers: config.headers,
45
78
  withCredentials: config.credentials === 'include',
46
- observe: 'body', // Only return body data
79
+ observe: 'response',
80
+ responseType: 'text',
47
81
  }));
82
+ const contentType = res.headers?.get('content-type');
83
+ const parsed = this.parseJsonBody(res.body ?? '', contentType);
48
84
  return {
49
- data,
50
- status: 200, // HttpClient only returns data on success
51
- headers: {}, // Can extract from observe: 'response' if needed
85
+ data: parsed,
86
+ status: res.status,
87
+ headers: {}, // Reserved for future header passthrough if needed
52
88
  };
53
89
  }
54
90
  catch (error) {
55
91
  if (error instanceof HttpErrorResponse) {
56
- // Convert Angular's HttpErrorResponse to NAuthClientError
57
- const errorData = error.error || {};
58
- const code = typeof errorData['code'] === 'string' ? errorData.code : NAuthErrorCode.INTERNAL_ERROR;
92
+ // Convert Angular's HttpErrorResponse to NAuthClientError.
93
+ // When using responseType 'text', `error.error` is typically a string.
94
+ const contentType = error.headers?.get('content-type') ?? null;
95
+ const rawBody = typeof error.error === 'string' ? error.error : '';
96
+ const parsedError = this.parseJsonBody(rawBody, contentType);
97
+ const errorData = typeof parsedError === 'object' && parsedError !== null ? parsedError : {};
98
+ const code = typeof errorData['code'] === 'string' ? errorData['code'] : NAuthErrorCode.INTERNAL_ERROR;
59
99
  const message = typeof errorData['message'] === 'string'
60
- ? errorData.message
61
- : error.message || `Request failed with status ${error.status}`;
62
- const timestamp = typeof errorData['timestamp'] === 'string' ? errorData.timestamp : undefined;
63
- const details = errorData['details'];
100
+ ? errorData['message']
101
+ : typeof parsedError === 'string' && parsedError.trim()
102
+ ? parsedError
103
+ : error.message || `Request failed with status ${error.status}`;
104
+ const timestamp = typeof errorData['timestamp'] === 'string' ? errorData['timestamp'] : undefined;
105
+ const details = typeof errorData['details'] === 'object' ? errorData['details'] : undefined;
64
106
  throw new NAuthClientError(code, message, {
65
107
  statusCode: error.status,
66
108
  timestamp,
@@ -68,8 +110,12 @@ export class AngularHttpAdapter {
68
110
  isNetworkError: error.status === 0, // Network error (no response from server)
69
111
  });
70
112
  }
71
- // Re-throw non-HTTP errors
72
- throw error;
113
+ // Re-throw non-HTTP errors as an SDK error so consumers don't see raw parser crashes.
114
+ const message = error instanceof Error ? error.message : 'Unknown error';
115
+ throw new NAuthClientError(NAuthErrorCode.INTERNAL_ERROR, message, {
116
+ statusCode: 0,
117
+ isNetworkError: true,
118
+ });
73
119
  }
74
120
  }
75
121
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AngularHttpAdapter, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
@@ -78,4 +124,4 @@ export class AngularHttpAdapter {
78
124
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AngularHttpAdapter, decorators: [{
79
125
  type: Injectable
80
126
  }], ctorParameters: () => [{ type: i1.HttpClient }] });
81
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC1hZGFwdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL25nbW9kdWxlL2h0dHAtYWRhcHRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzNDLE9BQU8sRUFBYyxpQkFBaUIsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3JFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDdEMsT0FBTyxFQUEwQyxnQkFBZ0IsRUFBRSxjQUFjLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQzs7O0FBRWpIOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJHO0FBRUgsTUFBTSxPQUFPLGtCQUFrQjtJQUNBO0lBQTdCLFlBQTZCLElBQWdCO1FBQWhCLFNBQUksR0FBSixJQUFJLENBQVk7SUFBRyxDQUFDO0lBRWpEOzs7Ozs7T0FNRztJQUNILEtBQUssQ0FBQyxPQUFPLENBQUksTUFBbUI7UUFDbEMsSUFBSSxDQUFDO1lBQ0gsMkRBQTJEO1lBQzNELE1BQU0sSUFBSSxHQUFHLE1BQU0sY0FBYyxDQUMvQixJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBSSxNQUFNLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxHQUFHLEVBQUU7Z0JBQzlDLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSTtnQkFDakIsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPO2dCQUN2QixlQUFlLEVBQUUsTUFBTSxDQUFDLFdBQVcsS0FBSyxTQUFTO2dCQUNqRCxPQUFPLEVBQUUsTUFBTSxFQUFFLHdCQUF3QjthQUMxQyxDQUFDLENBQ0gsQ0FBQztZQUVGLE9BQU87Z0JBQ0wsSUFBSTtnQkFDSixNQUFNLEVBQUUsR0FBRyxFQUFFLDBDQUEwQztnQkFDdkQsT0FBTyxFQUFFLEVBQUUsRUFBRSxpREFBaUQ7YUFDL0QsQ0FBQztRQUNKLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxLQUFLLFlBQVksaUJBQWlCLEVBQUUsQ0FBQztnQkFDdkMsMERBQTBEO2dCQUMxRCxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQztnQkFDcEMsTUFBTSxJQUFJLEdBQ1IsT0FBTyxTQUFTLENBQUMsTUFBTSxDQUFDLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBRSxTQUFTLENBQUMsSUFBdUIsQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDLGNBQWMsQ0FBQztnQkFDN0csTUFBTSxPQUFPLEdBQ1gsT0FBTyxTQUFTLENBQUMsU0FBUyxDQUFDLEtBQUssUUFBUTtvQkFDdEMsQ0FBQyxDQUFFLFNBQVMsQ0FBQyxPQUFrQjtvQkFDL0IsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLElBQUksOEJBQThCLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQztnQkFDcEUsTUFBTSxTQUFTLEdBQUcsT0FBTyxTQUFTLENBQUMsV0FBVyxDQUFDLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7Z0JBQy9GLE1BQU0sT0FBTyxHQUFHLFNBQVMsQ0FBQyxTQUFTLENBQXdDLENBQUM7Z0JBRTVFLE1BQU0sSUFBSSxnQkFBZ0IsQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFO29CQUN4QyxVQUFVLEVBQUUsS0FBSyxDQUFDLE1BQU07b0JBQ3hCLFNBQVM7b0JBQ1QsT0FBTztvQkFDUCxjQUFjLEVBQUUsS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsMENBQTBDO2lCQUMvRSxDQUFDLENBQUM7WUFDTCxDQUFDO1lBRUQsMkJBQTJCO1lBQzNCLE1BQU0sS0FBSyxDQUFDO1FBQ2QsQ0FBQztJQUNILENBQUM7d0dBbkRVLGtCQUFrQjs0R0FBbEIsa0JBQWtCOzs0RkFBbEIsa0JBQWtCO2tCQUQ5QixVQUFVIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgSHR0cENsaWVudCwgSHR0cEVycm9yUmVzcG9uc2UgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XG5pbXBvcnQgeyBmaXJzdFZhbHVlRnJvbSB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgSHR0cEFkYXB0ZXIsIEh0dHBSZXF1ZXN0LCBIdHRwUmVzcG9uc2UsIE5BdXRoQ2xpZW50RXJyb3IsIE5BdXRoRXJyb3JDb2RlIH0gZnJvbSAnQG5hdXRoLXRvb2xraXQvY2xpZW50JztcblxuLyoqXG4gKiBIVFRQIGFkYXB0ZXIgZm9yIEFuZ3VsYXIgdXNpbmcgSHR0cENsaWVudC5cbiAqXG4gKiBUaGlzIGFkYXB0ZXI6XG4gKiAtIFVzZXMgQW5ndWxhcidzIEh0dHBDbGllbnQgZm9yIGFsbCByZXF1ZXN0c1xuICogLSBXb3JrcyB3aXRoIEFuZ3VsYXIncyBIVFRQIGludGVyY2VwdG9ycyAoaW5jbHVkaW5nIGF1dGhJbnRlcmNlcHRvcilcbiAqIC0gQXV0by1wcm92aWRlZCB2aWEgQW5ndWxhciBESSAocHJvdmlkZWRJbjogJ3Jvb3QnKVxuICogLSBDb252ZXJ0cyBIdHRwQ2xpZW50IHJlc3BvbnNlcyB0byBIdHRwUmVzcG9uc2UgZm9ybWF0XG4gKiAtIENvbnZlcnRzIEh0dHBFcnJvclJlc3BvbnNlIHRvIE5BdXRoQ2xpZW50RXJyb3JcbiAqXG4gKiBVc2VycyBkb24ndCBuZWVkIHRvIGNvbmZpZ3VyZSB0aGlzIG1hbnVhbGx5IC0gaXQncyBhdXRvbWF0aWNhbGx5XG4gKiBpbmplY3RlZCB3aGVuIHVzaW5nIEF1dGhTZXJ2aWNlIGluIEFuZ3VsYXIgYXBwcy5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gQXV0b21hdGljIHVzYWdlIChubyBtYW51YWwgc2V0dXAgbmVlZGVkKVxuICogLy8gQXV0aFNlcnZpY2UgYXV0b21hdGljYWxseSBpbmplY3RzIEFuZ3VsYXJIdHRwQWRhcHRlclxuICogY29uc3RydWN0b3IocHJpdmF0ZSBhdXRoOiBBdXRoU2VydmljZSkge31cbiAqIGBgYFxuICovXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgQW5ndWxhckh0dHBBZGFwdGVyIGltcGxlbWVudHMgSHR0cEFkYXB0ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IGh0dHA6IEh0dHBDbGllbnQpIHt9XG5cbiAgLyoqXG4gICAqIEV4ZWN1dGUgSFRUUCByZXF1ZXN0IHVzaW5nIEFuZ3VsYXIncyBIdHRwQ2xpZW50LlxuICAgKlxuICAgKiBAcGFyYW0gY29uZmlnIC0gUmVxdWVzdCBjb25maWd1cmF0aW9uXG4gICAqIEByZXR1cm5zIFJlc3BvbnNlIHdpdGggcGFyc2VkIGRhdGFcbiAgICogQHRocm93cyBOQXV0aENsaWVudEVycm9yIGlmIHJlcXVlc3QgZmFpbHNcbiAgICovXG4gIGFzeW5jIHJlcXVlc3Q8VD4oY29uZmlnOiBIdHRwUmVxdWVzdCk6IFByb21pc2U8SHR0cFJlc3BvbnNlPFQ+PiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIFVzZSBBbmd1bGFyJ3MgSHR0cENsaWVudCAtIGdvZXMgdGhyb3VnaCBBTEwgaW50ZXJjZXB0b3JzXG4gICAgICBjb25zdCBkYXRhID0gYXdhaXQgZmlyc3RWYWx1ZUZyb20oXG4gICAgICAgIHRoaXMuaHR0cC5yZXF1ZXN0PFQ+KGNvbmZpZy5tZXRob2QsIGNvbmZpZy51cmwsIHtcbiAgICAgICAgICBib2R5OiBjb25maWcuYm9keSxcbiAgICAgICAgICBoZWFkZXJzOiBjb25maWcuaGVhZGVycyxcbiAgICAgICAgICB3aXRoQ3JlZGVudGlhbHM6IGNvbmZpZy5jcmVkZW50aWFscyA9PT0gJ2luY2x1ZGUnLFxuICAgICAgICAgIG9ic2VydmU6ICdib2R5JywgLy8gT25seSByZXR1cm4gYm9keSBkYXRhXG4gICAgICAgIH0pLFxuICAgICAgKTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgZGF0YSxcbiAgICAgICAgc3RhdHVzOiAyMDAsIC8vIEh0dHBDbGllbnQgb25seSByZXR1cm5zIGRhdGEgb24gc3VjY2Vzc1xuICAgICAgICBoZWFkZXJzOiB7fSwgLy8gQ2FuIGV4dHJhY3QgZnJvbSBvYnNlcnZlOiAncmVzcG9uc2UnIGlmIG5lZWRlZFxuICAgICAgfTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgSHR0cEVycm9yUmVzcG9uc2UpIHtcbiAgICAgICAgLy8gQ29udmVydCBBbmd1bGFyJ3MgSHR0cEVycm9yUmVzcG9uc2UgdG8gTkF1dGhDbGllbnRFcnJvclxuICAgICAgICBjb25zdCBlcnJvckRhdGEgPSBlcnJvci5lcnJvciB8fCB7fTtcbiAgICAgICAgY29uc3QgY29kZSA9XG4gICAgICAgICAgdHlwZW9mIGVycm9yRGF0YVsnY29kZSddID09PSAnc3RyaW5nJyA/IChlcnJvckRhdGEuY29kZSBhcyBOQXV0aEVycm9yQ29kZSkgOiBOQXV0aEVycm9yQ29kZS5JTlRFUk5BTF9FUlJPUjtcbiAgICAgICAgY29uc3QgbWVzc2FnZSA9XG4gICAgICAgICAgdHlwZW9mIGVycm9yRGF0YVsnbWVzc2FnZSddID09PSAnc3RyaW5nJ1xuICAgICAgICAgICAgPyAoZXJyb3JEYXRhLm1lc3NhZ2UgYXMgc3RyaW5nKVxuICAgICAgICAgICAgOiBlcnJvci5tZXNzYWdlIHx8IGBSZXF1ZXN0IGZhaWxlZCB3aXRoIHN0YXR1cyAke2Vycm9yLnN0YXR1c31gO1xuICAgICAgICBjb25zdCB0aW1lc3RhbXAgPSB0eXBlb2YgZXJyb3JEYXRhWyd0aW1lc3RhbXAnXSA9PT0gJ3N0cmluZycgPyBlcnJvckRhdGEudGltZXN0YW1wIDogdW5kZWZpbmVkO1xuICAgICAgICBjb25zdCBkZXRhaWxzID0gZXJyb3JEYXRhWydkZXRhaWxzJ10gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCB1bmRlZmluZWQ7XG5cbiAgICAgICAgdGhyb3cgbmV3IE5BdXRoQ2xpZW50RXJyb3IoY29kZSwgbWVzc2FnZSwge1xuICAgICAgICAgIHN0YXR1c0NvZGU6IGVycm9yLnN0YXR1cyxcbiAgICAgICAgICB0aW1lc3RhbXAsXG4gICAgICAgICAgZGV0YWlscyxcbiAgICAgICAgICBpc05ldHdvcmtFcnJvcjogZXJyb3Iuc3RhdHVzID09PSAwLCAvLyBOZXR3b3JrIGVycm9yIChubyByZXNwb25zZSBmcm9tIHNlcnZlcilcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIC8vIFJlLXRocm93IG5vbi1IVFRQIGVycm9yc1xuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9XG59XG4iXX0=
127
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC1hZGFwdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL25nbW9kdWxlL2h0dHAtYWRhcHRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzNDLE9BQU8sRUFBYyxpQkFBaUIsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3JFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDdEMsT0FBTyxFQUEwQyxnQkFBZ0IsRUFBRSxjQUFjLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQzs7O0FBRWpIOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUJHO0FBRUgsTUFBTSxPQUFPLGtCQUFrQjtJQUNBO0lBQTdCLFlBQTZCLElBQWdCO1FBQWhCLFNBQUksR0FBSixJQUFJLENBQVk7SUFBRyxDQUFDO0lBRWpEOzs7Ozs7Ozs7Ozs7OztPQWNHO0lBQ0ssYUFBYSxDQUFDLFFBQWdCLEVBQUUsV0FBMEI7UUFDaEUsTUFBTSxPQUFPLEdBQUcsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ2hDLElBQUksQ0FBQyxPQUFPO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFFMUIsdUZBQXVGO1FBQ3ZGLElBQUksT0FBTyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQzVCLE9BQU8sUUFBUSxDQUFDO1FBQ2xCLENBQUM7UUFFRCxNQUFNLGFBQWEsR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDekUsTUFBTSxpQkFBaUIsR0FBRyxPQUFPLFdBQVcsS0FBSyxRQUFRLElBQUksV0FBVyxDQUFDLFdBQVcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO1FBRXBILElBQUksQ0FBQyxhQUFhLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxDQUFD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QVMsQ0FBQztnQkFFM0csTUFBTSxJQUFJLGdCQUFnQixDQUFDLElBQUksRUFBRSxPQUFPLEVBQUU7b0JBQ3hDLFVBQVUsRUFBRSxLQUFLLENBQUMsTUFBTTtvQkFDeEIsU0FBUztvQkFDVCxPQUFPO29CQUNQLGNBQWMsRUFBRSxLQUFLLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSwwQ0FBMEM7aUJBQy9FLENBQUMsQ0FBQztZQUNMLENBQUM7WUFFRCxzRkFBc0Y7WUFDdEYsTUFBTSxPQUFPLEdBQUcsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsZUFBZSxDQUFDO1lBQ3pFLE1BQU0sSUFBSSxnQkFBZ0IsQ0FBQyxjQUFjLENBQUMsY0FBYyxFQUFFLE9BQU8sRUFBRTtnQkFDakUsVUFBVSxFQUFFLENBQUM7Z0JBQ2IsY0FBYyxFQUFFLElBQUk7YUFDckIsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztJQUNILENBQUM7d0dBekdVLGtCQUFrQjs0R0FBbEIsa0JBQWtCOzs0RkFBbEIsa0JBQWtCO2tCQUQ5QixVQUFVIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgSHR0cENsaWVudCwgSHR0cEVycm9yUmVzcG9uc2UgfSBmcm9tICdAYW5ndWxhci9jb21tb24vaHR0cCc7XG5pbXBvcnQgeyBmaXJzdFZhbHVlRnJvbSB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgSHR0cEFkYXB0ZXIsIEh0dHBSZXF1ZXN0LCBIdHRwUmVzcG9uc2UsIE5BdXRoQ2xpZW50RXJyb3IsIE5BdXRoRXJyb3JDb2RlIH0gZnJvbSAnQG5hdXRoLXRvb2xraXQvY2xpZW50JztcblxuLyoqXG4gKiBIVFRQIGFkYXB0ZXIgZm9yIEFuZ3VsYXIgdXNpbmcgSHR0cENsaWVudC5cbiAqXG4gKiBUaGlzIGFkYXB0ZXI6XG4gKiAtIFVzZXMgQW5ndWxhcidzIEh0dHBDbGllbnQgZm9yIGFsbCByZXF1ZXN0c1xuICogLSBXb3JrcyB3aXRoIEFuZ3VsYXIncyBIVFRQIGludGVyY2VwdG9ycyAoaW5jbHVkaW5nIGF1dGhJbnRlcmNlcHRvcilcbiAqIC0gQXV0by1wcm92aWRlZCB2aWEgQW5ndWxhciBESSAocHJvdmlkZWRJbjogJ3Jvb3QnKVxuICogLSBDb252ZXJ0cyBIdHRwQ2xpZW50IHJlc3BvbnNlcyB0byBIdHRwUmVzcG9uc2UgZm9ybWF0XG4gKiAtIENvbnZlcnRzIEh0dHBFcnJvclJlc3BvbnNlIHRvIE5BdXRoQ2xpZW50RXJyb3JcbiAqXG4gKiBVc2VycyBkb24ndCBuZWVkIHRvIGNvbmZpZ3VyZSB0aGlzIG1hbnVhbGx5IC0gaXQncyBhdXRvbWF0aWNhbGx5XG4gKiBpbmplY3RlZCB3aGVuIHVzaW5nIEF1dGhTZXJ2aWNlIGluIEFuZ3VsYXIgYXBwcy5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gQXV0b21hdGljIHVzYWdlIChubyBtYW51YWwgc2V0dXAgbmVlZGVkKVxuICogLy8gQXV0aFNlcnZpY2UgYXV0b21hdGljYWxseSBpbmplY3RzIEFuZ3VsYXJIdHRwQWRhcHRlclxuICogY29uc3RydWN0b3IocHJpdmF0ZSBhdXRoOiBBdXRoU2VydmljZSkge31cbiAqIGBgYFxuICovXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgQW5ndWxhckh0dHBBZGFwdGVyIGltcGxlbWVudHMgSHR0cEFkYXB0ZXIge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IGh0dHA6IEh0dHBDbGllbnQpIHt9XG5cbiAgLyoqXG4gICAqIFNhZmVseSBwYXJzZSBhIEpTT04gcmVzcG9uc2UgYm9keS5cbiAgICpcbiAgICogQW5ndWxhcidzIGZldGNoIGJhY2tlbmQgKGB3aXRoRmV0Y2goKWApIHdpbGwgdGhyb3cgYSByYXcgYFN5bnRheEVycm9yYCBpZlxuICAgKiBgcmVzcG9uc2VUeXBlOiAnanNvbidgIGlzIHVzZWQgYW5kIHRoZSBiYWNrZW5kIHJldHVybnMgSFRNTCAoY29tbW9uIGZvclxuICAgKiBwcm94aWVzLCA1MDIgcGFnZXMsIFNTUiBmYWxsYmFja3MsIG9yIG1pc3JvdXRlZCByZXF1ZXN0cykuXG4gICAqXG4gICAqIFRvIGF2b2lkIGNyYXNoaW5nIGNvbnN1bWVyIGFwcHMsIHdlIGFsd2F5cyByZXF1ZXN0IGFzIHRleHQgYW5kIHRoZW4gcGFyc2VcbiAgICogSlNPTiBvbmx5IHdoZW4gdGhlIHJlc3BvbnNlIGFjdHVhbGx5IGxvb2tzIGxpa2UgSlNPTi5cbiAgICpcbiAgICogQHBhcmFtIGJvZHlUZXh0IC0gUmF3IHJlc3BvbnNlIGJvZHkgYXMgdGV4dFxuICAgKiBAcGFyYW0gY29udGVudFR5cGUgLSBDb250ZW50LVR5cGUgaGVhZGVyIHZhbHVlIChpZiBhdmFpbGFibGUpXG4gICAqIEByZXR1cm5zIFBhcnNlZCBKU09OIHZhbHVlICh1bmtub3duKVxuICAgKiBAdGhyb3dzIHtTeW50YXhFcnJvcn0gV2hlbiBib2R5IGlzIG5vbi1lbXB0eSBidXQgbm90IHZhbGlkIEpTT05cbiAgICovXG4gIHByaXZhdGUgcGFyc2VKc29uQm9keShib2R5VGV4dDogc3RyaW5nLCBjb250ZW50VHlwZTogc3RyaW5nIHwgbnVsbCk6IHVua25vd24ge1xuICAgIGNvbnN0IHRyaW1tZWQgPSBib2R5VGV4dC50cmltKCk7XG4gICAgaWYgKCF0cmltbWVkKSByZXR1cm4gbnVsbDtcblxuICAgIC8vIElmIGl0J3MgY2xlYXJseSBIVE1MLCBuZXZlciBhdHRlbXB0IEpTT04ucGFyc2UgKHNvbWUgcHJveGllcyBtaXNsYWJlbCBDb250ZW50LVR5cGUpLlxuICAgIGlmICh0cmltbWVkLnN0YXJ0c1dpdGgoJzwnKSkge1xuICAgICAgcmV0dXJuIGJvZHlUZXh0O1xuICAgIH1cblxuICAgIGNvbnN0IGxvb2tzTGlrZUpzb24gPSB0cmltbWVkLnN0YXJ0c1dpdGgoJ3snKSB8fCB0cmltbWVkLnN0YXJ0c1dpdGgoJ1snKTtcbiAgICBjb25zdCBpc0pzb25Db250ZW50VHlwZSA9IHR5cGVvZiBjb250ZW50VHlwZSA9PT0gJ3N0cmluZycgJiYgY29udGVudFR5cGUudG9Mb3dlckNhc2UoKS5pbmNsdWRlcygnYXBwbGljYXRpb24vanNvbicpO1xuXG4gICAgaWYgKCFsb29rc0xpa2VKc29uICYmICFpc0pzb25Db250ZW50VHlwZSkge1xuICAgICAgLy8gUmV0dXJuIHJhdyB0ZXh0IHdoZW4gaXQgZG9lc24ndCBsb29rIGxpa2UgSlNPTiAoZS5nLiwgSFRNTCBlcnJvciBwYWdlcykuXG4gICAgICByZXR1cm4gYm9keVRleHQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIEpTT04ucGFyc2UodHJpbW1lZCkgYXMgdW5rbm93bjtcbiAgfVxuXG4gIC8qKlxuICAgKiBFeGVjdXRlIEhUVFAgcmVxdWVzdCB1c2luZyBBbmd1bGFyJ3MgSHR0cENsaWVudC5cbiAgICpcbiAgICogQHBhcmFtIGNvbmZpZyAtIFJlcXVlc3QgY29uZmlndXJhdGlvblxuICAgKiBAcmV0dXJucyBSZXNwb25zZSB3aXRoIHBhcnNlZCBkYXRhXG4gICAqIEB0aHJvd3MgTkF1dGhDbGllbnRFcnJvciBpZiByZXF1ZXN0IGZhaWxzXG4gICAqL1xuICBhc3luYyByZXF1ZXN0PFQ+KGNvbmZpZzogSHR0cFJlcXVlc3QpOiBQcm9taXNlPEh0dHBSZXNwb25zZTxUPj4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBVc2UgQW5ndWxhcidzIEh0dHBDbGllbnQgLSBnb2VzIHRocm91Z2ggQUxMIGludGVyY2VwdG9ycy5cbiAgICAgIC8vIElNUE9SVEFOVDogVXNlIHJlc3BvbnNlVHlwZSAndGV4dCcgdG8gYXZvaWQgcmF3IEpTT04ucGFyc2UgY3Jhc2hlcyB3aGVuXG4gICAgICAvLyB0aGUgYmFja2VuZCByZXR1cm5zIEhUTUwgKHNlZW4gaW4gc29tZSBwcm94eS9TU1IvbWlzcm91dGUgc2V0dXBzKS5cbiAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IGZpcnN0VmFsdWVGcm9tKFxuICAgICAgICB0aGlzLmh0dHAucmVxdWVzdChjb25maWcubWV0aG9kLCBjb25maWcudXJsLCB7XG4gICAgICAgICAgYm9keTogY29uZmlnLmJvZHksXG4gICAgICAgICAgaGVhZGVyczogY29uZmlnLmhlYWRlcnMsXG4gICAgICAgICAgd2l0aENyZWRlbnRpYWxzOiBjb25maWcuY3JlZGVudGlhbHMgPT09ICdpbmNsdWRlJyxcbiAgICAgICAgICBvYnNlcnZlOiAncmVzcG9uc2UnLFxuICAgICAgICAgIHJlc3BvbnNlVHlwZTogJ3RleHQnLFxuICAgICAgICB9KSxcbiAgICAgICk7XG5cbiAgICAgIGNvbnN0IGNvbnRlbnRUeXBlID0gcmVzLmhlYWRlcnM/LmdldCgnY29udGVudC10eXBlJyk7XG4gICAgICBjb25zdCBwYXJzZWQgPSB0aGlzLnBhcnNlSnNvbkJvZHkocmVzLmJvZHkgPz8gJycsIGNvbnRlbnRUeXBlKTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgZGF0YTogcGFyc2VkIGFzIFQsXG4gICAgICAgIHN0YXR1czogcmVzLnN0YXR1cyxcbiAgICAgICAgaGVhZGVyczoge30sIC8vIFJlc2VydmVkIGZvciBmdXR1cmUgaGVhZGVyIHBhc3N0aHJvdWdoIGlmIG5lZWRlZFxuICAgICAgfTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgSHR0cEVycm9yUmVzcG9uc2UpIHtcbiAgICAgICAgLy8gQ29udmVydCBBbmd1bGFyJ3MgSHR0cEVycm9yUmVzcG9uc2UgdG8gTkF1dGhDbGllbnRFcnJvci5cbiAgICAgICAgLy8gV2hlbiB1c2luZyByZXNwb25zZVR5cGUgJ3RleHQnLCBgZXJyb3IuZXJyb3JgIGlzIHR5cGljYWxseSBhIHN0cmluZy5cbiAgICAgICAgY29uc3QgY29udGVudFR5cGUgPSBlcnJvci5oZWFkZXJzPy5nZXQoJ2NvbnRlbnQtdHlwZScpID8/IG51bGw7XG4gICAgICAgIGNvbnN0IHJhd0JvZHkgPSB0eXBlb2YgZXJyb3IuZXJyb3IgPT09ICdzdHJpbmcnID8gZXJyb3IuZXJyb3IgOiAnJztcbiAgICAgICAgY29uc3QgcGFyc2VkRXJyb3IgPSB0aGlzLnBhcnNlSnNvbkJvZHkocmF3Qm9keSwgY29udGVudFR5cGUpO1xuXG4gICAgICAgIGNvbnN0IGVycm9yRGF0YSA9XG4gICAgICAgICAgdHlwZW9mIHBhcnNlZEVycm9yID09PSAnb2JqZWN0JyAmJiBwYXJzZWRFcnJvciAhPT0gbnVsbCA/IChwYXJzZWRFcnJvciBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgOiB7fTtcbiAgICAgICAgY29uc3QgY29kZSA9XG4gICAgICAgICAgdHlwZW9mIGVycm9yRGF0YVsnY29kZSddID09PSAnc3RyaW5nJyA/IChlcnJvckRhdGFbJ2NvZGUnXSBhcyBOQXV0aEVycm9yQ29kZSkgOiBOQXV0aEVycm9yQ29kZS5JTlRFUk5BTF9FUlJPUjtcbiAgICAgICAgY29uc3QgbWVzc2FnZSA9XG4gICAgICAgICAgdHlwZW9mIGVycm9yRGF0YVsnbWVzc2FnZSddID09PSAnc3RyaW5nJ1xuICAgICAgICAgICAgPyAoZXJyb3JEYXRhWydtZXNzYWdlJ10gYXMgc3RyaW5nKVxuICAgICAgICAgICAgOiB0eXBlb2YgcGFyc2VkRXJyb3IgPT09ICdzdHJpbmcnICYmIHBhcnNlZEVycm9yLnRyaW0oKVxuICAgICAgICAgICAgICA/IHBhcnNlZEVycm9yXG4gICAgICAgICAgICAgIDogZXJyb3IubWVzc2FnZSB8fCBgUmVxdWVzdCBmYWlsZWQgd2l0aCBzdGF0dXMgJHtlcnJvci5zdGF0dXN9YDtcbiAgICAgICAgY29uc3QgdGltZXN0YW1wID0gdHlwZW9mIGVycm9yRGF0YVsndGltZXN0YW1wJ10gPT09ICdzdHJpbmcnID8gKGVycm9yRGF0YVsndGltZXN0YW1wJ10gYXMgc3RyaW5nKSA6IHVuZGVmaW5lZDtcbiAgICAgICAgY29uc3QgZGV0YWlscyA9XG4gICAgICAgICAgdHlwZW9mIGVycm9yRGF0YVsnZGV0YWlscyddID09PSAnb2JqZWN0JyA/IChlcnJvckRhdGFbJ2RldGFpbHMnXSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgOiB1bmRlZmluZWQ7XG5cbiAgICAgICAgdGhyb3cgbmV3IE5BdXRoQ2xpZW50RXJyb3IoY29kZSwgbWVzc2FnZSwge1xuICAgICAgICAgIHN0YXR1c0NvZGU6IGVycm9yLnN0YXR1cyxcbiAgICAgICAgICB0aW1lc3RhbXAsXG4gICAgICAgICAgZGV0YWlscyxcbiAgICAgICAgICBpc05ldHdvcmtFcnJvcjogZXJyb3Iuc3RhdHVzID09PSAwLCAvLyBOZXR3b3JrIGVycm9yIChubyByZXNwb25zZSBmcm9tIHNlcnZlcilcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIC8vIFJlLXRocm93IG5vbi1IVFRQIGVycm9ycyBhcyBhbiBTREsgZXJyb3Igc28gY29uc3VtZXJzIGRvbid0IHNlZSByYXcgcGFyc2VyIGNyYXNoZXMuXG4gICAgICBjb25zdCBtZXNzYWdlID0gZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiAnVW5rbm93biBlcnJvcic7XG4gICAgICB0aHJvdyBuZXcgTkF1dGhDbGllbnRFcnJvcihOQXV0aEVycm9yQ29kZS5JTlRFUk5BTF9FUlJPUiwgbWVzc2FnZSwge1xuICAgICAgICBzdGF0dXNDb2RlOiAwLFxuICAgICAgICBpc05ldHdvcmtFcnJvcjogdHJ1ZSxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxufVxuIl19
@@ -29,6 +29,37 @@ export class AngularHttpAdapter {
29
29
  constructor(http) {
30
30
  this.http = http;
31
31
  }
32
+ /**
33
+ * Safely parse a JSON response body.
34
+ *
35
+ * Angular's fetch backend (`withFetch()`) will throw a raw `SyntaxError` if
36
+ * `responseType: 'json'` is used and the backend returns HTML (common for
37
+ * proxies, 502 pages, SSR fallbacks, or misrouted requests).
38
+ *
39
+ * To avoid crashing consumer apps, we always request as text and then parse
40
+ * JSON only when the response actually looks like JSON.
41
+ *
42
+ * @param bodyText - Raw response body as text
43
+ * @param contentType - Content-Type header value (if available)
44
+ * @returns Parsed JSON value (unknown)
45
+ * @throws {SyntaxError} When body is non-empty but not valid JSON
46
+ */
47
+ parseJsonBody(bodyText, contentType) {
48
+ const trimmed = bodyText.trim();
49
+ if (!trimmed)
50
+ return null;
51
+ // If it's clearly HTML, never attempt JSON.parse (some proxies mislabel Content-Type).
52
+ if (trimmed.startsWith('<')) {
53
+ return bodyText;
54
+ }
55
+ const looksLikeJson = trimmed.startsWith('{') || trimmed.startsWith('[');
56
+ const isJsonContentType = typeof contentType === 'string' && contentType.toLowerCase().includes('application/json');
57
+ if (!looksLikeJson && !isJsonContentType) {
58
+ // Return raw text when it doesn't look like JSON (e.g., HTML error pages).
59
+ return bodyText;
60
+ }
61
+ return JSON.parse(trimmed);
62
+ }
32
63
  /**
33
64
  * Execute HTTP request using Angular's HttpClient.
34
65
  *
@@ -38,29 +69,40 @@ export class AngularHttpAdapter {
38
69
  */
39
70
  async request(config) {
40
71
  try {
41
- // Use Angular's HttpClient - goes through ALL interceptors
42
- const data = await firstValueFrom(this.http.request(config.method, config.url, {
72
+ // Use Angular's HttpClient - goes through ALL interceptors.
73
+ // IMPORTANT: Use responseType 'text' to avoid raw JSON.parse crashes when
74
+ // the backend returns HTML (seen in some proxy/SSR/misroute setups).
75
+ const res = await firstValueFrom(this.http.request(config.method, config.url, {
43
76
  body: config.body,
44
77
  headers: config.headers,
45
78
  withCredentials: config.credentials === 'include',
46
- observe: 'body', // Only return body data
79
+ observe: 'response',
80
+ responseType: 'text',
47
81
  }));
82
+ const contentType = res.headers?.get('content-type');
83
+ const parsed = this.parseJsonBody(res.body ?? '', contentType);
48
84
  return {
49
- data,
50
- status: 200, // HttpClient only returns data on success
51
- headers: {}, // Can extract from observe: 'response' if needed
85
+ data: parsed,
86
+ status: res.status,
87
+ headers: {}, // Reserved for future header passthrough if needed
52
88
  };
53
89
  }
54
90
  catch (error) {
55
91
  if (error instanceof HttpErrorResponse) {
56
- // Convert Angular's HttpErrorResponse to NAuthClientError
57
- const errorData = error.error || {};
58
- const code = typeof errorData['code'] === 'string' ? errorData.code : NAuthErrorCode.INTERNAL_ERROR;
92
+ // Convert Angular's HttpErrorResponse to NAuthClientError.
93
+ // When using responseType 'text', `error.error` is typically a string.
94
+ const contentType = error.headers?.get('content-type') ?? null;
95
+ const rawBody = typeof error.error === 'string' ? error.error : '';
96
+ const parsedError = this.parseJsonBody(rawBody, contentType);
97
+ const errorData = typeof parsedError === 'object' && parsedError !== null ? parsedError : {};
98
+ const code = typeof errorData['code'] === 'string' ? errorData['code'] : NAuthErrorCode.INTERNAL_ERROR;
59
99
  const message = typeof errorData['message'] === 'string'
60
- ? errorData.message
61
- : error.message || `Request failed with status ${error.status}`;
62
- const timestamp = typeof errorData['timestamp'] === 'string' ? errorData.timestamp : undefined;
63
- const details = errorData['details'];
100
+ ? errorData['message']
101
+ : typeof parsedError === 'string' && parsedError.trim()
102
+ ? parsedError
103
+ : error.message || `Request failed with status ${error.status}`;
104
+ const timestamp = typeof errorData['timestamp'] === 'string' ? errorData['timestamp'] : undefined;
105
+ const details = typeof errorData['details'] === 'object' ? errorData['details'] : undefined;
64
106
  throw new NAuthClientError(code, message, {
65
107
  statusCode: error.status,
66
108
  timestamp,
@@ -68,8 +110,12 @@ export class AngularHttpAdapter {
68
110
  isNetworkError: error.status === 0, // Network error (no response from server)
69
111
  });
70
112
  }
71
- // Re-throw non-HTTP errors
72
- throw error;
113
+ // Re-throw non-HTTP errors as an SDK error so consumers don't see raw parser crashes.
114
+ const message = error instanceof Error ? error.message : 'Unknown error';
115
+ throw new NAuthClientError(NAuthErrorCode.INTERNAL_ERROR, message, {
116
+ statusCode: 0,
117
+ isNetworkError: true,
118
+ });
73
119
  }
74
120
  }
75
121
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AngularHttpAdapter, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
@@ -78,4 +124,4 @@ export class AngularHttpAdapter {
78
124
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AngularHttpAdapter, decorators: [{
79
125
  type: Injectable
80
126
  }], ctorParameters: () => [{ type: i1.HttpClient }] });
81
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC1hZGFwdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3RhbmRhbG9uZS9odHRwLWFkYXB0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBQWMsaUJBQWlCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNyRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3RDLE9BQU8sRUFBMEMsZ0JBQWdCLEVBQUUsY0FBYyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7OztBQUVqSDs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUVILE1BQU0sT0FBTyxrQkFBa0I7SUFDQTtJQUE3QixZQUE2QixJQUFnQjtRQUFoQixTQUFJLEdBQUosSUFBSSxDQUFZO0lBQUcsQ0FBQztJQUVqRDs7Ozs7O09BTUc7SUFDSCxLQUFLLENBQUMsT0FBTyxDQUFJLE1BQW1CO1FBQ2xDLElBQUksQ0FBQztZQUNILDJEQUEyRDtZQUMzRCxNQUFNLElBQUksR0FBRyxNQUFNLGNBQWMsQ0FDL0IsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUksTUFBTSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsR0FBRyxFQUFFO2dCQUM5QyxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUk7Z0JBQ2pCLE9BQU8sRUFBRSxNQUFNLENBQUMsT0FBTztnQkFDdkIsZUFBZSxFQUFFLE1BQU0sQ0FBQyxXQUFXLEtBQUssU0FBUztnQkFDakQsT0FBTyxFQUFFLE1BQU0sRUFBRSx3QkFBd0I7YUFDMUMsQ0FBQyxDQUNILENBQUM7WUFFRixPQUFPO2dCQUNMLElBQUk7Z0JBQ0osTUFBTSxFQUFFLEdBQUcsRUFBRSwwQ0FBMEM7Z0JBQ3ZELE9BQU8sRUFBRSxFQUFFLEVBQUUsaURBQWlEO2FBQy9ELENBQUM7UUFDSixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLElBQUksS0FBSyxZQUFZLGlCQUFpQixFQUFFLENBQUM7Z0JBQ3ZDLDBEQUEwRDtnQkFDMUQsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLEtBQUssSUFBSSxFQUFFLENBQUM7Z0JBQ3BDLE1BQU0sSUFBSSxHQUNSLE9BQU8sU0FBUyxDQUFDLE1BQU0sQ0FBQyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUUsU0FBUyxDQUFDLElBQXVCLENBQUMsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxjQUFjLENBQUM7Z0JBQzdHLE1BQU0sT0FBTyxHQUNYLE9BQU8sU0FBUyxDQUFDLFNBQVMsQ0FBQyxLQUFLLFFBQVE7b0JBQ3RDLENBQUMsQ0FBRSxTQUFTLENBQUMsT0FBa0I7b0JBQy9CLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxJQUFJLDhCQUE4QixLQUFLLENBQUMsTUFBTSxFQUFFLENBQUM7Z0JBQ3BFLE1BQU0sU0FBUyxHQUFHLE9BQU8sU0FBUyxDQUFDLFdBQVcsQ0FBQyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO2dCQUMvRixNQUFNLE9BQU8sR0FBRyxTQUFTLENBQUMsU0FBUyxDQUF3QyxDQUFDO2dCQUU1RSxNQUFNLElBQUksZ0JBQWdCLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRTtvQkFDeEMsVUFBVSxFQUFFLEtBQUssQ0FBQyxNQUFNO29CQUN4QixTQUFTO29CQUNULE9BQU87b0JBQ1AsY0FBYyxFQUFFLEtBQUssQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLDBDQUEwQztpQkFDL0UsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztZQUVELDJCQUEyQjtZQUMzQixNQUFNLEtBQUssQ0FBQztRQUNkLENBQUM7SUFDSCxDQUFDO3dHQW5EVSxrQkFBa0I7NEdBQWxCLGtCQUFrQjs7NEZBQWxCLGtCQUFrQjtrQkFEOUIsVUFBVSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEh0dHBDbGllbnQsIEh0dHBFcnJvclJlc3BvbnNlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xuaW1wb3J0IHsgZmlyc3RWYWx1ZUZyb20gfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IEh0dHBBZGFwdGVyLCBIdHRwUmVxdWVzdCwgSHR0cFJlc3BvbnNlLCBOQXV0aENsaWVudEVycm9yLCBOQXV0aEVycm9yQ29kZSB9IGZyb20gJ0BuYXV0aC10b29sa2l0L2NsaWVudCc7XG5cbi8qKlxuICogSFRUUCBhZGFwdGVyIGZvciBBbmd1bGFyIHVzaW5nIEh0dHBDbGllbnQuXG4gKlxuICogVGhpcyBhZGFwdGVyOlxuICogLSBVc2VzIEFuZ3VsYXIncyBIdHRwQ2xpZW50IGZvciBhbGwgcmVxdWVzdHNcbiAqIC0gV29ya3Mgd2l0aCBBbmd1bGFyJ3MgSFRUUCBpbnRlcmNlcHRvcnMgKGluY2x1ZGluZyBhdXRoSW50ZXJjZXB0b3IpXG4gKiAtIEF1dG8tcHJvdmlkZWQgdmlhIEFuZ3VsYXIgREkgKHByb3ZpZGVkSW46ICdyb290JylcbiAqIC0gQ29udmVydHMgSHR0cENsaWVudCByZXNwb25zZXMgdG8gSHR0cFJlc3BvbnNlIGZvcm1hdFxuICogLSBDb252ZXJ0cyBIdHRwRXJyb3JSZXNwb25zZSB0byBOQXV0aENsaWVudEVycm9yXG4gKlxuICogVXNlcnMgZG9uJ3QgbmVlZCB0byBjb25maWd1cmUgdGhpcyBtYW51YWxseSAtIGl0J3MgYXV0b21hdGljYWxseVxuICogaW5qZWN0ZWQgd2hlbiB1c2luZyBBdXRoU2VydmljZSBpbiBBbmd1bGFyIGFwcHMuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIEF1dG9tYXRpYyB1c2FnZSAobm8gbWFudWFsIHNldHVwIG5lZWRlZClcbiAqIC8vIEF1dGhTZXJ2aWNlIGF1dG9tYXRpY2FsbHkgaW5qZWN0cyBBbmd1bGFySHR0cEFkYXB0ZXJcbiAqIGNvbnN0cnVjdG9yKHByaXZhdGUgYXV0aDogQXV0aFNlcnZpY2UpIHt9XG4gKiBgYGBcbiAqL1xuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIEFuZ3VsYXJIdHRwQWRhcHRlciBpbXBsZW1lbnRzIEh0dHBBZGFwdGVyIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBodHRwOiBIdHRwQ2xpZW50KSB7fVxuXG4gIC8qKlxuICAgKiBFeGVjdXRlIEhUVFAgcmVxdWVzdCB1c2luZyBBbmd1bGFyJ3MgSHR0cENsaWVudC5cbiAgICpcbiAgICogQHBhcmFtIGNvbmZpZyAtIFJlcXVlc3QgY29uZmlndXJhdGlvblxuICAgKiBAcmV0dXJucyBSZXNwb25zZSB3aXRoIHBhcnNlZCBkYXRhXG4gICAqIEB0aHJvd3MgTkF1dGhDbGllbnRFcnJvciBpZiByZXF1ZXN0IGZhaWxzXG4gICAqL1xuICBhc3luYyByZXF1ZXN0PFQ+KGNvbmZpZzogSHR0cFJlcXVlc3QpOiBQcm9taXNlPEh0dHBSZXNwb25zZTxUPj4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBVc2UgQW5ndWxhcidzIEh0dHBDbGllbnQgLSBnb2VzIHRocm91Z2ggQUxMIGludGVyY2VwdG9yc1xuICAgICAgY29uc3QgZGF0YSA9IGF3YWl0IGZpcnN0VmFsdWVGcm9tKFxuICAgICAgICB0aGlzLmh0dHAucmVxdWVzdDxUPihjb25maWcubWV0aG9kLCBjb25maWcudXJsLCB7XG4gICAgICAgICAgYm9keTogY29uZmlnLmJvZHksXG4gICAgICAgICAgaGVhZGVyczogY29uZmlnLmhlYWRlcnMsXG4gICAgICAgICAgd2l0aENyZWRlbnRpYWxzOiBjb25maWcuY3JlZGVudGlhbHMgPT09ICdpbmNsdWRlJyxcbiAgICAgICAgICBvYnNlcnZlOiAnYm9keScsIC8vIE9ubHkgcmV0dXJuIGJvZHkgZGF0YVxuICAgICAgICB9KSxcbiAgICAgICk7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGRhdGEsXG4gICAgICAgIHN0YXR1czogMjAwLCAvLyBIdHRwQ2xpZW50IG9ubHkgcmV0dXJucyBkYXRhIG9uIHN1Y2Nlc3NcbiAgICAgICAgaGVhZGVyczoge30sIC8vIENhbiBleHRyYWN0IGZyb20gb2JzZXJ2ZTogJ3Jlc3BvbnNlJyBpZiBuZWVkZWRcbiAgICAgIH07XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIEh0dHBFcnJvclJlc3BvbnNlKSB7XG4gICAgICAgIC8vIENvbnZlcnQgQW5ndWxhcidzIEh0dHBFcnJvclJlc3BvbnNlIHRvIE5BdXRoQ2xpZW50RXJyb3JcbiAgICAgICAgY29uc3QgZXJyb3JEYXRhID0gZXJyb3IuZXJyb3IgfHwge307XG4gICAgICAgIGNvbnN0IGNvZGUgPVxuICAgICAgICAgIHR5cGVvZiBlcnJvckRhdGFbJ2NvZGUnXSA9PT0gJ3N0cmluZycgPyAoZXJyb3JEYXRhLmNvZGUgYXMgTkF1dGhFcnJvckNvZGUpIDogTkF1dGhFcnJvckNvZGUuSU5URVJOQUxfRVJST1I7XG4gICAgICAgIGNvbnN0IG1lc3NhZ2UgPVxuICAgICAgICAgIHR5cGVvZiBlcnJvckRhdGFbJ21lc3NhZ2UnXSA9PT0gJ3N0cmluZydcbiAgICAgICAgICAgID8gKGVycm9yRGF0YS5tZXNzYWdlIGFzIHN0cmluZylcbiAgICAgICAgICAgIDogZXJyb3IubWVzc2FnZSB8fCBgUmVxdWVzdCBmYWlsZWQgd2l0aCBzdGF0dXMgJHtlcnJvci5zdGF0dXN9YDtcbiAgICAgICAgY29uc3QgdGltZXN0YW1wID0gdHlwZW9mIGVycm9yRGF0YVsndGltZXN0YW1wJ10gPT09ICdzdHJpbmcnID8gZXJyb3JEYXRhLnRpbWVzdGFtcCA6IHVuZGVmaW5lZDtcbiAgICAgICAgY29uc3QgZGV0YWlscyA9IGVycm9yRGF0YVsnZGV0YWlscyddIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgdW5kZWZpbmVkO1xuXG4gICAgICAgIHRocm93IG5ldyBOQXV0aENsaWVudEVycm9yKGNvZGUsIG1lc3NhZ2UsIHtcbiAgICAgICAgICBzdGF0dXNDb2RlOiBlcnJvci5zdGF0dXMsXG4gICAgICAgICAgdGltZXN0YW1wLFxuICAgICAgICAgIGRldGFpbHMsXG4gICAgICAgICAgaXNOZXR3b3JrRXJyb3I6IGVycm9yLnN0YXR1cyA9PT0gMCwgLy8gTmV0d29yayBlcnJvciAobm8gcmVzcG9uc2UgZnJvbSBzZXJ2ZXIpXG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICAvLyBSZS10aHJvdyBub24tSFRUUCBlcnJvcnNcbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgfVxufVxuIl19
127
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC1hZGFwdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3RhbmRhbG9uZS9odHRwLWFkYXB0ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUMzQyxPQUFPLEVBQWMsaUJBQWlCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNyRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3RDLE9BQU8sRUFBMEMsZ0JBQWdCLEVBQUUsY0FBYyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7OztBQUVqSDs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUVILE1BQU0sT0FBTyxrQkFBa0I7SUFDQTtJQUE3QixZQUE2QixJQUFnQjtRQUFoQixTQUFJLEdBQUosSUFBSSxDQUFZO0lBQUcsQ0FBQztJQUVqRDs7Ozs7Ozs7Ozs7Ozs7T0FjRztJQUNLLGFBQWEsQ0FBQyxRQUFnQixFQUFFLFdBQTBCO1FBQ2hFLE1BQU0sT0FBTyxHQUFHLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNoQyxJQUFJLENBQUMsT0FBTztZQUFFLE9BQU8sSUFBSSxDQUFDO1FBRTFCLHVGQUF1RjtRQUN2RixJQUFJLE9BQU8sQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUM1QixPQUFPLFFBQVEsQ0FBQztRQUNsQixDQUFDO1FBRUQsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsSUFBSSxPQUFPLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3pFLE1BQU0saUJBQWlCLEdBQUcsT0FBTyxXQUFXLEtBQUssUUFBUSxJQUFJLFdBQVcsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxRQUFRLENBQUMsa0JBQWtCLENBQUMsQ0FBQztRQUVwSCxJQUFJLENBQUMsYUFBYSxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztZ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LENBQUM7Z0JBRXpILE1BQU0sSUFBSSxnQkFBZ0IsQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFO29CQUN4QyxVQUFVLEVBQUUsS0FBSyxDQUFDLE1BQU07b0JBQ3hCLFNBQVM7b0JBQ1QsT0FBTztvQkFDUCxjQUFjLEVBQUUsS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsMENBQTBDO2lCQUMvRSxDQUFDLENBQUM7WUFDTCxDQUFDO1lBRUQsc0ZBQXNGO1lBQ3RGLE1BQU0sT0FBTyxHQUFHLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLGVBQWUsQ0FBQztZQUN6RSxNQUFNLElBQUksZ0JBQWdCLENBQUMsY0FBYyxDQUFDLGNBQWMsRUFBRSxPQUFPLEVBQUU7Z0JBQ2pFLFVBQVUsRUFBRSxDQUFDO2dCQUNiLGNBQWMsRUFBRSxJQUFJO2FBQ3JCLENBQUMsQ0FBQztRQUNMLENBQUM7SUFDSCxDQUFDO3dHQXRHVSxrQkFBa0I7NEdBQWxCLGtCQUFrQjs7NEZBQWxCLGtCQUFrQjtrQkFEOUIsVUFBVSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEh0dHBDbGllbnQsIEh0dHBFcnJvclJlc3BvbnNlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uL2h0dHAnO1xuaW1wb3J0IHsgZmlyc3RWYWx1ZUZyb20gfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IEh0dHBBZGFwdGVyLCBIdHRwUmVxdWVzdCwgSHR0cFJlc3BvbnNlLCBOQXV0aENsaWVudEVycm9yLCBOQXV0aEVycm9yQ29kZSB9IGZyb20gJ0BuYXV0aC10b29sa2l0L2NsaWVudCc7XG5cbi8qKlxuICogSFRUUCBhZGFwdGVyIGZvciBBbmd1bGFyIHVzaW5nIEh0dHBDbGllbnQuXG4gKlxuICogVGhpcyBhZGFwdGVyOlxuICogLSBVc2VzIEFuZ3VsYXIncyBIdHRwQ2xpZW50IGZvciBhbGwgcmVxdWVzdHNcbiAqIC0gV29ya3Mgd2l0aCBBbmd1bGFyJ3MgSFRUUCBpbnRlcmNlcHRvcnMgKGluY2x1ZGluZyBhdXRoSW50ZXJjZXB0b3IpXG4gKiAtIEF1dG8tcHJvdmlkZWQgdmlhIEFuZ3VsYXIgREkgKHByb3ZpZGVkSW46ICdyb290JylcbiAqIC0gQ29udmVydHMgSHR0cENsaWVudCByZXNwb25zZXMgdG8gSHR0cFJlc3BvbnNlIGZvcm1hdFxuICogLSBDb252ZXJ0cyBIdHRwRXJyb3JSZXNwb25zZSB0byBOQXV0aENsaWVudEVycm9yXG4gKlxuICogVXNlcnMgZG9uJ3QgbmVlZCB0byBjb25maWd1cmUgdGhpcyBtYW51YWxseSAtIGl0J3MgYXV0b21hdGljYWxseVxuICogaW5qZWN0ZWQgd2hlbiB1c2luZyBBdXRoU2VydmljZSBpbiBBbmd1bGFyIGFwcHMuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIEF1dG9tYXRpYyB1c2FnZSAobm8gbWFudWFsIHNldHVwIG5lZWRlZClcbiAqIC8vIEF1dGhTZXJ2aWNlIGF1dG9tYXRpY2FsbHkgaW5qZWN0cyBBbmd1bGFySHR0cEFkYXB0ZXJcbiAqIGNvbnN0cnVjdG9yKHByaXZhdGUgYXV0aDogQXV0aFNlcnZpY2UpIHt9XG4gKiBgYGBcbiAqL1xuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIEFuZ3VsYXJIdHRwQWRhcHRlciBpbXBsZW1lbnRzIEh0dHBBZGFwdGVyIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBodHRwOiBIdHRwQ2xpZW50KSB7fVxuXG4gIC8qKlxuICAgKiBTYWZlbHkgcGFyc2UgYSBKU09OIHJlc3BvbnNlIGJvZHkuXG4gICAqXG4gICAqIEFuZ3VsYXIncyBmZXRjaCBiYWNrZW5kIChgd2l0aEZldGNoKClgKSB3aWxsIHRocm93IGEgcmF3IGBTeW50YXhFcnJvcmAgaWZcbiAgICogYHJlc3BvbnNlVHlwZTogJ2pzb24nYCBpcyB1c2VkIGFuZCB0aGUgYmFja2VuZCByZXR1cm5zIEhUTUwgKGNvbW1vbiBmb3JcbiAgICogcHJveGllcywgNTAyIHBhZ2VzLCBTU1IgZmFsbGJhY2tzLCBvciBtaXNyb3V0ZWQgcmVxdWVzdHMpLlxuICAgKlxuICAgKiBUbyBhdm9pZCBjcmFzaGluZyBjb25zdW1lciBhcHBzLCB3ZSBhbHdheXMgcmVxdWVzdCBhcyB0ZXh0IGFuZCB0aGVuIHBhcnNlXG4gICAqIEpTT04gb25seSB3aGVuIHRoZSByZXNwb25zZSBhY3R1YWxseSBsb29rcyBsaWtlIEpTT04uXG4gICAqXG4gICAqIEBwYXJhbSBib2R5VGV4dCAtIFJhdyByZXNwb25zZSBib2R5IGFzIHRleHRcbiAgICogQHBhcmFtIGNvbnRlbnRUeXBlIC0gQ29udGVudC1UeXBlIGhlYWRlciB2YWx1ZSAoaWYgYXZhaWxhYmxlKVxuICAgKiBAcmV0dXJucyBQYXJzZWQgSlNPTiB2YWx1ZSAodW5rbm93bilcbiAgICogQHRocm93cyB7U3ludGF4RXJyb3J9IFdoZW4gYm9keSBpcyBub24tZW1wdHkgYnV0IG5vdCB2YWxpZCBKU09OXG4gICAqL1xuICBwcml2YXRlIHBhcnNlSnNvbkJvZHkoYm9keVRleHQ6IHN0cmluZywgY29udGVudFR5cGU6IHN0cmluZyB8IG51bGwpOiB1bmtub3duIHtcbiAgICBjb25zdCB0cmltbWVkID0gYm9keVRleHQudHJpbSgpO1xuICAgIGlmICghdHJpbW1lZCkgcmV0dXJuIG51bGw7XG5cbiAgICAvLyBJZiBpdCdzIGNsZWFybHkgSFRNTCwgbmV2ZXIgYXR0ZW1wdCBKU09OLnBhcnNlIChzb21lIHByb3hpZXMgbWlzbGFiZWwgQ29udGVudC1UeXBlKS5cbiAgICBpZiAodHJpbW1lZC5zdGFydHNXaXRoKCc8JykpIHtcbiAgICAgIHJldHVybiBib2R5VGV4dDtcbiAgICB9XG5cbiAgICBjb25zdCBsb29rc0xpa2VKc29uID0gdHJpbW1lZC5zdGFydHNXaXRoKCd7JykgfHwgdHJpbW1lZC5zdGFydHNXaXRoKCdbJyk7XG4gICAgY29uc3QgaXNKc29uQ29udGVudFR5cGUgPSB0eXBlb2YgY29udGVudFR5cGUgPT09ICdzdHJpbmcnICYmIGNvbnRlbnRUeXBlLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMoJ2FwcGxpY2F0aW9uL2pzb24nKTtcblxuICAgIGlmICghbG9va3NMaWtlSnNvbiAmJiAhaXNKc29uQ29udGVudFR5cGUpIHtcbiAgICAgIC8vIFJldHVybiByYXcgdGV4dCB3aGVuIGl0IGRvZXNuJ3QgbG9vayBsaWtlIEpTT04gKGUuZy4sIEhUTUwgZXJyb3IgcGFnZXMpLlxuICAgICAgcmV0dXJuIGJvZHlUZXh0O1xuICAgIH1cblxuICAgIHJldHVybiBKU09OLnBhcnNlKHRyaW1tZWQpIGFzIHVua25vd247XG4gIH1cblxuICAvKipcbiAgICogRXhlY3V0ZSBIVFRQIHJlcXVlc3QgdXNpbmcgQW5ndWxhcidzIEh0dHBDbGllbnQuXG4gICAqXG4gICAqIEBwYXJhbSBjb25maWcgLSBSZXF1ZXN0IGNvbmZpZ3VyYXRpb25cbiAgICogQHJldHVybnMgUmVzcG9uc2Ugd2l0aCBwYXJzZWQgZGF0YVxuICAgKiBAdGhyb3dzIE5BdXRoQ2xpZW50RXJyb3IgaWYgcmVxdWVzdCBmYWlsc1xuICAgKi9cbiAgYXN5bmMgcmVxdWVzdDxUPihjb25maWc6IEh0dHBSZXF1ZXN0KTogUHJvbWlzZTxIdHRwUmVzcG9uc2U8VD4+IHtcbiAgICB0cnkge1xuICAgICAgLy8gVXNlIEFuZ3VsYXIncyBIdHRwQ2xpZW50IC0gZ29lcyB0aHJvdWdoIEFMTCBpbnRlcmNlcHRvcnMuXG4gICAgICAvLyBJTVBPUlRBTlQ6IFVzZSByZXNwb25zZVR5cGUgJ3RleHQnIHRvIGF2b2lkIHJhdyBKU09OLnBhcnNlIGNyYXNoZXMgd2hlblxuICAgICAgLy8gdGhlIGJhY2tlbmQgcmV0dXJucyBIVE1MIChzZWVuIGluIHNvbWUgcHJveHkvU1NSL21pc3JvdXRlIHNldHVwcykuXG4gICAgICBjb25zdCByZXMgPSBhd2FpdCBmaXJzdFZhbHVlRnJvbShcbiAgICAgICAgdGhpcy5odHRwLnJlcXVlc3QoY29uZmlnLm1ldGhvZCwgY29uZmlnLnVybCwge1xuICAgICAgICAgIGJvZHk6IGNvbmZpZy5ib2R5LFxuICAgICAgICAgIGhlYWRlcnM6IGNvbmZpZy5oZWFkZXJzLFxuICAgICAgICAgIHdpdGhDcmVkZW50aWFsczogY29uZmlnLmNyZWRlbnRpYWxzID09PSAnaW5jbHVkZScsXG4gICAgICAgICAgb2JzZXJ2ZTogJ3Jlc3BvbnNlJyxcbiAgICAgICAgICByZXNwb25zZVR5cGU6ICd0ZXh0JyxcbiAgICAgICAgfSksXG4gICAgICApO1xuXG4gICAgICBjb25zdCBjb250ZW50VHlwZSA9IHJlcy5oZWFkZXJzPy5nZXQoJ2NvbnRlbnQtdHlwZScpO1xuICAgICAgY29uc3QgcGFyc2VkID0gdGhpcy5wYXJzZUpzb25Cb2R5KHJlcy5ib2R5ID8/ICcnLCBjb250ZW50VHlwZSk7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGRhdGE6IHBhcnNlZCBhcyBULFxuICAgICAgICBzdGF0dXM6IHJlcy5zdGF0dXMsXG4gICAgICAgIGhlYWRlcnM6IHt9LCAvLyBSZXNlcnZlZCBmb3IgZnV0dXJlIGhlYWRlciBwYXNzdGhyb3VnaCBpZiBuZWVkZWRcbiAgICAgIH07XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIEh0dHBFcnJvclJlc3BvbnNlKSB7XG4gICAgICAgIC8vIENvbnZlcnQgQW5ndWxhcidzIEh0dHBFcnJvclJlc3BvbnNlIHRvIE5BdXRoQ2xpZW50RXJyb3IuXG4gICAgICAgIC8vIFdoZW4gdXNpbmcgcmVzcG9uc2VUeXBlICd0ZXh0JywgYGVycm9yLmVycm9yYCBpcyB0eXBpY2FsbHkgYSBzdHJpbmcuXG4gICAgICAgIGNvbnN0IGNvbnRlbnRUeXBlID0gZXJyb3IuaGVhZGVycz8uZ2V0KCdjb250ZW50LXR5cGUnKSA/PyBudWxsO1xuICAgICAgICBjb25zdCByYXdCb2R5ID0gdHlwZW9mIGVycm9yLmVycm9yID09PSAnc3RyaW5nJyA/IGVycm9yLmVycm9yIDogJyc7XG4gICAgICAgIGNvbnN0IHBhcnNlZEVycm9yID0gdGhpcy5wYXJzZUpzb25Cb2R5KHJhd0JvZHksIGNvbnRlbnRUeXBlKTtcblxuICAgICAgICBjb25zdCBlcnJvckRhdGEgPSB0eXBlb2YgcGFyc2VkRXJyb3IgPT09ICdvYmplY3QnICYmIHBhcnNlZEVycm9yICE9PSBudWxsID8gKHBhcnNlZEVycm9yIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KSA6IHt9O1xuICAgICAgICBjb25zdCBjb2RlID0gdHlwZW9mIGVycm9yRGF0YVsnY29kZSddID09PSAnc3RyaW5nJyA/IChlcnJvckRhdGFbJ2NvZGUnXSBhcyBOQXV0aEVycm9yQ29kZSkgOiBOQXV0aEVycm9yQ29kZS5JTlRFUk5BTF9FUlJPUjtcbiAgICAgICAgY29uc3QgbWVzc2FnZSA9XG4gICAgICAgICAgdHlwZW9mIGVycm9yRGF0YVsnbWVzc2FnZSddID09PSAnc3RyaW5nJ1xuICAgICAgICAgICAgPyAoZXJyb3JEYXRhWydtZXNzYWdlJ10gYXMgc3RyaW5nKVxuICAgICAgICAgICAgOiB0eXBlb2YgcGFyc2VkRXJyb3IgPT09ICdzdHJpbmcnICYmIHBhcnNlZEVycm9yLnRyaW0oKVxuICAgICAgICAgICAgICA/IHBhcnNlZEVycm9yXG4gICAgICAgICAgICAgIDogZXJyb3IubWVzc2FnZSB8fCBgUmVxdWVzdCBmYWlsZWQgd2l0aCBzdGF0dXMgJHtlcnJvci5zdGF0dXN9YDtcbiAgICAgICAgY29uc3QgdGltZXN0YW1wID0gdHlwZW9mIGVycm9yRGF0YVsndGltZXN0YW1wJ10gPT09ICdzdHJpbmcnID8gKGVycm9yRGF0YVsndGltZXN0YW1wJ10gYXMgc3RyaW5nKSA6IHVuZGVmaW5lZDtcbiAgICAgICAgY29uc3QgZGV0YWlscyA9IHR5cGVvZiBlcnJvckRhdGFbJ2RldGFpbHMnXSA9PT0gJ29iamVjdCcgPyAoZXJyb3JEYXRhWydkZXRhaWxzJ10gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pIDogdW5kZWZpbmVkO1xuXG4gICAgICAgIHRocm93IG5ldyBOQXV0aENsaWVudEVycm9yKGNvZGUsIG1lc3NhZ2UsIHtcbiAgICAgICAgICBzdGF0dXNDb2RlOiBlcnJvci5zdGF0dXMsXG4gICAgICAgICAgdGltZXN0YW1wLFxuICAgICAgICAgIGRldGFpbHMsXG4gICAgICAgICAgaXNOZXR3b3JrRXJyb3I6IGVycm9yLnN0YXR1cyA9PT0gMCwgLy8gTmV0d29yayBlcnJvciAobm8gcmVzcG9uc2UgZnJvbSBzZXJ2ZXIpXG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICAvLyBSZS10aHJvdyBub24tSFRUUCBlcnJvcnMgYXMgYW4gU0RLIGVycm9yIHNvIGNvbnN1bWVycyBkb24ndCBzZWUgcmF3IHBhcnNlciBjcmFzaGVzLlxuICAgICAgY29uc3QgbWVzc2FnZSA9IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogJ1Vua25vd24gZXJyb3InO1xuICAgICAgdGhyb3cgbmV3IE5BdXRoQ2xpZW50RXJyb3IoTkF1dGhFcnJvckNvZGUuSU5URVJOQUxfRVJST1IsIG1lc3NhZ2UsIHtcbiAgICAgICAgc3RhdHVzQ29kZTogMCxcbiAgICAgICAgaXNOZXR3b3JrRXJyb3I6IHRydWUsXG4gICAgICB9KTtcbiAgICB9XG4gIH1cbn1cbiJdfQ==
@@ -39,6 +39,37 @@ class AngularHttpAdapter {
39
39
  constructor(http) {
40
40
  this.http = http;
41
41
  }
42
+ /**
43
+ * Safely parse a JSON response body.
44
+ *
45
+ * Angular's fetch backend (`withFetch()`) will throw a raw `SyntaxError` if
46
+ * `responseType: 'json'` is used and the backend returns HTML (common for
47
+ * proxies, 502 pages, SSR fallbacks, or misrouted requests).
48
+ *
49
+ * To avoid crashing consumer apps, we always request as text and then parse
50
+ * JSON only when the response actually looks like JSON.
51
+ *
52
+ * @param bodyText - Raw response body as text
53
+ * @param contentType - Content-Type header value (if available)
54
+ * @returns Parsed JSON value (unknown)
55
+ * @throws {SyntaxError} When body is non-empty but not valid JSON
56
+ */
57
+ parseJsonBody(bodyText, contentType) {
58
+ const trimmed = bodyText.trim();
59
+ if (!trimmed)
60
+ return null;
61
+ // If it's clearly HTML, never attempt JSON.parse (some proxies mislabel Content-Type).
62
+ if (trimmed.startsWith('<')) {
63
+ return bodyText;
64
+ }
65
+ const looksLikeJson = trimmed.startsWith('{') || trimmed.startsWith('[');
66
+ const isJsonContentType = typeof contentType === 'string' && contentType.toLowerCase().includes('application/json');
67
+ if (!looksLikeJson && !isJsonContentType) {
68
+ // Return raw text when it doesn't look like JSON (e.g., HTML error pages).
69
+ return bodyText;
70
+ }
71
+ return JSON.parse(trimmed);
72
+ }
42
73
  /**
43
74
  * Execute HTTP request using Angular's HttpClient.
44
75
  *
@@ -48,29 +79,40 @@ class AngularHttpAdapter {
48
79
  */
49
80
  async request(config) {
50
81
  try {
51
- // Use Angular's HttpClient - goes through ALL interceptors
52
- const data = await firstValueFrom(this.http.request(config.method, config.url, {
82
+ // Use Angular's HttpClient - goes through ALL interceptors.
83
+ // IMPORTANT: Use responseType 'text' to avoid raw JSON.parse crashes when
84
+ // the backend returns HTML (seen in some proxy/SSR/misroute setups).
85
+ const res = await firstValueFrom(this.http.request(config.method, config.url, {
53
86
  body: config.body,
54
87
  headers: config.headers,
55
88
  withCredentials: config.credentials === 'include',
56
- observe: 'body', // Only return body data
89
+ observe: 'response',
90
+ responseType: 'text',
57
91
  }));
92
+ const contentType = res.headers?.get('content-type');
93
+ const parsed = this.parseJsonBody(res.body ?? '', contentType);
58
94
  return {
59
- data,
60
- status: 200, // HttpClient only returns data on success
61
- headers: {}, // Can extract from observe: 'response' if needed
95
+ data: parsed,
96
+ status: res.status,
97
+ headers: {}, // Reserved for future header passthrough if needed
62
98
  };
63
99
  }
64
100
  catch (error) {
65
101
  if (error instanceof HttpErrorResponse) {
66
- // Convert Angular's HttpErrorResponse to NAuthClientError
67
- const errorData = error.error || {};
68
- const code = typeof errorData['code'] === 'string' ? errorData.code : NAuthErrorCode.INTERNAL_ERROR;
102
+ // Convert Angular's HttpErrorResponse to NAuthClientError.
103
+ // When using responseType 'text', `error.error` is typically a string.
104
+ const contentType = error.headers?.get('content-type') ?? null;
105
+ const rawBody = typeof error.error === 'string' ? error.error : '';
106
+ const parsedError = this.parseJsonBody(rawBody, contentType);
107
+ const errorData = typeof parsedError === 'object' && parsedError !== null ? parsedError : {};
108
+ const code = typeof errorData['code'] === 'string' ? errorData['code'] : NAuthErrorCode.INTERNAL_ERROR;
69
109
  const message = typeof errorData['message'] === 'string'
70
- ? errorData.message
71
- : error.message || `Request failed with status ${error.status}`;
72
- const timestamp = typeof errorData['timestamp'] === 'string' ? errorData.timestamp : undefined;
73
- const details = errorData['details'];
110
+ ? errorData['message']
111
+ : typeof parsedError === 'string' && parsedError.trim()
112
+ ? parsedError
113
+ : error.message || `Request failed with status ${error.status}`;
114
+ const timestamp = typeof errorData['timestamp'] === 'string' ? errorData['timestamp'] : undefined;
115
+ const details = typeof errorData['details'] === 'object' ? errorData['details'] : undefined;
74
116
  throw new NAuthClientError(code, message, {
75
117
  statusCode: error.status,
76
118
  timestamp,
@@ -78,8 +120,12 @@ class AngularHttpAdapter {
78
120
  isNetworkError: error.status === 0, // Network error (no response from server)
79
121
  });
80
122
  }
81
- // Re-throw non-HTTP errors
82
- throw error;
123
+ // Re-throw non-HTTP errors as an SDK error so consumers don't see raw parser crashes.
124
+ const message = error instanceof Error ? error.message : 'Unknown error';
125
+ throw new NAuthClientError(NAuthErrorCode.INTERNAL_ERROR, message, {
126
+ statusCode: 0,
127
+ isNetworkError: true,
128
+ });
83
129
  }
84
130
  }
85
131
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: AngularHttpAdapter, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });