@adobe/helix-onedrive-support 12.2.3 → 12.3.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [12.3.0](https://github.com/adobe/helix-onedrive-support/compare/v12.2.3...v12.3.0) (2026-03-10)
2
+
3
+
4
+ ### Features
5
+
6
+ * **msal:** use custom network module and @adobe/fetch ([#729](https://github.com/adobe/helix-onedrive-support/issues/729)) ([6dc88a7](https://github.com/adobe/helix-onedrive-support/commit/6dc88a70a0447214b82aa3e6d59d7da7e97cb052))
7
+
1
8
  ## [12.2.3](https://github.com/adobe/helix-onedrive-support/compare/v12.2.2...v12.2.3) (2026-03-04)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-onedrive-support",
3
- "version": "12.2.3",
3
+ "version": "12.3.0",
4
4
  "description": "Helix OneDrive Support",
5
5
  "main": "src/index.js",
6
6
  "exports": {
@@ -27,15 +27,16 @@
27
27
  },
28
28
  "homepage": "https://github.com/adobe/helix-onedrive-support#readme",
29
29
  "dependencies": {
30
- "@adobe/fetch": "^4.1.10",
30
+ "@adobe/fetch": "^4.2.3",
31
31
  "@adobe/helix-shared-string": "^2.1.0",
32
32
  "@adobe/helix-shared-tokencache": "^1.4.19",
33
+ "@azure/msal-common": "16.0.4",
33
34
  "@azure/msal-node": "5.0.4",
34
35
  "jose": "6.1.3"
35
36
  },
36
37
  "devDependencies": {
37
38
  "@adobe/eslint-config-helix": "3.0.21",
38
- "@aws-sdk/client-s3": "3.991.0",
39
+ "@aws-sdk/client-s3": "3.996.0",
39
40
  "@eslint/config-helpers": "0.5.2",
40
41
  "@semantic-release/changelog": "6.0.3",
41
42
  "@semantic-release/git": "10.0.1",
@@ -54,7 +55,7 @@
54
55
  "mocha-multi-reporters": "1.5.1",
55
56
  "mocha-suppress-logs": "0.6.0",
56
57
  "nock": "14.0.11",
57
- "npm": "11.10.0",
58
+ "npm": "11.10.1",
58
59
  "semantic-release": "25.0.3"
59
60
  },
60
61
  "lint-staged": {
@@ -0,0 +1,169 @@
1
+ /*
2
+ * Copyright 2026 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+ import { Headers } from '@adobe/fetch';
13
+ import { createAuthError, ClientAuthErrorCodes, createNetworkError } from '@azure/msal-common/node';
14
+
15
+ /**
16
+ * HTTP methods
17
+ */
18
+ const HttpMethod = {
19
+ GET: 'GET',
20
+ POST: 'POST',
21
+ };
22
+
23
+ /**
24
+ * Converts a fetch Headers object to a plain JavaScript object.
25
+ *
26
+ * The fetch API returns headers as a Headers object with methods like get(), has(),
27
+ * etc. However, the rest of the MSAL codebase expects headers as a simple key-value
28
+ * object. This function performs that conversion.
29
+ *
30
+ * @param headers - The Headers object returned by fetch response
31
+ * @returns A plain object with header names as keys and values as strings
32
+ */
33
+ function getHeaderDict(headers) {
34
+ const headerDict = {};
35
+ headers.forEach((value, key) => {
36
+ headerDict[key] = value;
37
+ });
38
+ return headerDict;
39
+ }
40
+ /**
41
+ * Converts NetworkRequestOptions headers to a fetch-compatible Headers object.
42
+ *
43
+ * The MSAL library uses plain objects for headers in NetworkRequestOptions,
44
+ * but the fetch API expects either a Headers object, plain object, or array
45
+ * of arrays. Using the Headers constructor provides better compatibility
46
+ * and validation.
47
+ *
48
+ * @param options - Optional NetworkRequestOptions containing headers
49
+ * @returns A Headers object ready for use with fetch API
50
+ */
51
+ function getFetchHeaders(options) {
52
+ const headers = new Headers();
53
+ if (options?.headers) {
54
+ Object.entries(options.headers).forEach(([key, value]) => {
55
+ headers.append(key, value);
56
+ });
57
+ }
58
+ return headers;
59
+ }
60
+
61
+ /**
62
+ * Custom network module implementation using @adobe/fetch.
63
+ */
64
+ export class CustomNetworkModule {
65
+ /**
66
+ * @constructor
67
+ * @param {import('@adobe/fetch').FetchContext} fetchContext fetch context
68
+ */
69
+ constructor(fetchContext) {
70
+ this.fetchContext = fetchContext;
71
+ }
72
+
73
+ /**
74
+ * Sends an HTTP GET request to the specified URL.
75
+ *
76
+ * This method handles GET requests with optional timeout support. The timeout
77
+ * is implemented using AbortController, which provides a clean way to cancel
78
+ * fetch requests that take too long to complete.
79
+ *
80
+ * @param url - The target URL for the GET request
81
+ * @param options - Optional request configuration including headers
82
+ * @param timeout - Optional timeout in milliseconds. If specified, the request
83
+ * will be aborted if it doesn't complete within this time
84
+ * @returns Promise that resolves to a NetworkResponse containing headers, body, and status
85
+ * @throws {AuthError} When the request times out or response parsing fails
86
+ * @throws {NetworkError} When the network request fails
87
+ */
88
+ async sendGetRequestAsync(url, options, timeout) {
89
+ return this.sendRequest(url, HttpMethod.GET, options, timeout);
90
+ }
91
+
92
+ /**
93
+ * Sends an HTTP POST request to the specified URL.
94
+ *
95
+ * This method handles POST requests with request body support. Currently,
96
+ * timeout functionality is not exposed for POST requests, but the underlying
97
+ * implementation supports it through the shared sendRequest method.
98
+ *
99
+ * @param url - The target URL for the POST request
100
+ * @param options - Optional request configuration including headers and body
101
+ * @returns Promise that resolves to a NetworkResponse containing headers, body, and status
102
+ * @throws {AuthError} When the request times out or response parsing fails
103
+ * @throws {NetworkError} When the network request fails
104
+ */
105
+ async sendPostRequestAsync(url, options) {
106
+ return this.sendRequest(url, HttpMethod.POST, options);
107
+ }
108
+
109
+ /**
110
+ * Send an HTTP request to the specified URL.
111
+ *
112
+ * @param url - The target URL for the request
113
+ * @param method - HTTP method (GET or POST)
114
+ * @param options - Optional request configuration (headers, body)
115
+ * @param timeout - Optional timeout in milliseconds for request cancellation
116
+ * @returns Promise resolving to NetworkResponse with parsed JSON body
117
+ * @throws {AuthError} For timeouts or JSON parsing errors
118
+ * @throws {NetworkError} For network failures
119
+ */
120
+ async sendRequest(url, method, options, timeout) {
121
+ const fetchOptions = {
122
+ method,
123
+ headers: getFetchHeaders(options),
124
+ };
125
+ /*
126
+ * Configure timeout if specified
127
+ * The setTimeout will trigger abort() if the request takes too long
128
+ */
129
+ let timerId;
130
+ if (timeout) {
131
+ const controller = new AbortController();
132
+ timerId = setTimeout(() => controller.abort(), timeout);
133
+ fetchOptions.signal = controller.signal;
134
+ }
135
+ if (method === HttpMethod.POST) {
136
+ fetchOptions.body = options?.body || '';
137
+ }
138
+
139
+ let response;
140
+ try {
141
+ const { fetch } = this.fetchContext;
142
+ response = await fetch(url, fetchOptions);
143
+ } catch (error) {
144
+ // Clean up timeout to prevent memory leaks
145
+ if (timerId) {
146
+ clearTimeout(timerId);
147
+ }
148
+ if (error instanceof Error && error.name === 'AbortError') {
149
+ throw createAuthError(ClientAuthErrorCodes.networkError, 'Request timeout');
150
+ }
151
+ const baseAuthError = createAuthError(ClientAuthErrorCodes.networkError, `Network request failed: ${error instanceof Error ? error.message : /* c8 ignore next */ 'unknown'}`);
152
+ throw createNetworkError(
153
+ baseAuthError,
154
+ undefined,
155
+ undefined,
156
+ error instanceof Error ? error : /* c8 ignore next */ undefined,
157
+ );
158
+ }
159
+ try {
160
+ return {
161
+ headers: getHeaderDict(response.headers),
162
+ body: (await response.json()),
163
+ status: response.status,
164
+ };
165
+ } catch (error) {
166
+ throw createAuthError(ClientAuthErrorCodes.tokenParsingError, `Failed to parse response: ${error instanceof Error ? error.message : /* c8 ignore next */ 'unknown'}`);
167
+ }
168
+ }
169
+ }
@@ -16,6 +16,7 @@ import { ConfidentialClientApplication, LogLevel, PublicClientApplication } from
16
16
  import { MemCachePlugin } from '@adobe/helix-shared-tokencache';
17
17
  import { decodeJwt } from 'jose';
18
18
  import { StatusCodeError } from './StatusCodeError.js';
19
+ import { CustomNetworkModule } from './CustomNetworkModule.js';
19
20
 
20
21
  const AZ_AUTHORITY_HOST_URL = 'https://login.windows.net';
21
22
  const AZ_COMMON_TENANT = 'common';
@@ -155,6 +156,7 @@ export class OneDriveAuth {
155
156
  piiLoggingEnabled: false,
156
157
  logLevel: LogLevel.Info,
157
158
  },
159
+ networkClient: new CustomNetworkModule(this.fetchContext),
158
160
  },
159
161
  };
160
162
  if (cachePlugin) {