@doist/twist-sdk 2.9.2 → 2.9.4
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/cjs/testUtils/msw-setup.js +67 -0
- package/dist/cjs/transport/fetch-with-retry.js +18 -12
- package/dist/cjs/transport/http-dispatcher.js +74 -19
- package/dist/esm/testUtils/msw-setup.js +68 -1
- package/dist/esm/transport/fetch-with-retry.js +19 -13
- package/dist/esm/transport/http-dispatcher.js +73 -27
- package/dist/types/transport/http-dispatcher.d.ts +36 -0
- package/package.json +1 -1
|
@@ -1,8 +1,75 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
24
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
2
49
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
50
|
exports.server = void 0;
|
|
4
51
|
var node_1 = require("msw/node");
|
|
5
52
|
var vitest_1 = require("vitest");
|
|
53
|
+
// In production the transport uses undici's own `fetch` (paired with the
|
|
54
|
+
// dispatcher) so the request client and dispatcher stay on one undici version.
|
|
55
|
+
// MSW intercepts the *global* `fetch`, not that separate undici instance, so
|
|
56
|
+
// force the transport onto the global `fetch` for every test in the suite;
|
|
57
|
+
// MSW then intercepts requests as usual. The real undici transport is covered
|
|
58
|
+
// directly by `http-dispatcher.test.ts` and `fetch-with-retry.test.ts`, which
|
|
59
|
+
// opt out of this seam.
|
|
60
|
+
vitest_1.vi.mock('../transport/http-dispatcher', function (importOriginal) { return __awaiter(void 0, void 0, void 0, function () {
|
|
61
|
+
var actual;
|
|
62
|
+
return __generator(this, function (_a) {
|
|
63
|
+
switch (_a.label) {
|
|
64
|
+
case 0: return [4 /*yield*/, importOriginal()];
|
|
65
|
+
case 1:
|
|
66
|
+
actual = _a.sent();
|
|
67
|
+
return [2 /*return*/, __assign(__assign({}, actual), { getDefaultTransport: vitest_1.vi.fn(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
68
|
+
return [2 /*return*/, undefined];
|
|
69
|
+
}); }); }) })];
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}); });
|
|
6
73
|
// Create MSW server instance
|
|
7
74
|
exports.server = (0, node_1.setupServer)();
|
|
8
75
|
// Start server before all tests
|
|
@@ -137,23 +137,29 @@ function fetchWithRetry(url_1, options_1) {
|
|
|
137
137
|
}
|
|
138
138
|
function fetchWithDefaultTransport(url, options, signal) {
|
|
139
139
|
return __awaiter(this, void 0, void 0, function () {
|
|
140
|
-
var
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
140
|
+
var transport, fetchImpl, response, _a;
|
|
141
|
+
var _b;
|
|
142
|
+
return __generator(this, function (_c) {
|
|
143
|
+
switch (_c.label) {
|
|
144
|
+
case 0: return [4 /*yield*/, (0, http_dispatcher_1.getDefaultTransport)()
|
|
145
|
+
// undici's `fetch` and the global `fetch` are the same function at runtime
|
|
146
|
+
// but carry different (undici vs DOM) `RequestInit`/`Response` types. Call
|
|
147
|
+
// through the global signature, which matches the global-typed `options`.
|
|
148
|
+
];
|
|
144
149
|
case 1:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
150
|
+
transport = _c.sent();
|
|
151
|
+
fetchImpl = ((_b = transport === null || transport === void 0 ? void 0 : transport.fetch) !== null && _b !== void 0 ? _b : fetch);
|
|
152
|
+
if (!(transport === null || transport === void 0 ? void 0 : transport.dispatcher)) return [3 /*break*/, 3];
|
|
153
|
+
return [4 /*yield*/, fetchImpl(url, __assign(__assign({}, options), { signal: signal,
|
|
148
154
|
// @ts-expect-error - dispatcher is valid for Node.js fetch but not in TS types
|
|
149
|
-
dispatcher: dispatcher }))];
|
|
155
|
+
dispatcher: transport.dispatcher }))];
|
|
150
156
|
case 2:
|
|
151
|
-
_a =
|
|
157
|
+
_a = _c.sent();
|
|
152
158
|
return [3 /*break*/, 5];
|
|
153
|
-
case 3: return [4 /*yield*/,
|
|
159
|
+
case 3: return [4 /*yield*/, fetchImpl(url, __assign(__assign({}, options), { signal: signal }))];
|
|
154
160
|
case 4:
|
|
155
|
-
_a =
|
|
156
|
-
|
|
161
|
+
_a = _c.sent();
|
|
162
|
+
_c.label = 5;
|
|
157
163
|
case 5:
|
|
158
164
|
response = _a;
|
|
159
165
|
return [2 /*return*/, convertResponseToCustomFetch(response)];
|
|
@@ -78,8 +78,10 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
78
78
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
79
79
|
};
|
|
80
80
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
81
|
+
exports.getDefaultTransport = getDefaultTransport;
|
|
81
82
|
exports.getDefaultDispatcher = getDefaultDispatcher;
|
|
82
83
|
exports.resetDefaultDispatcherForTests = resetDefaultDispatcherForTests;
|
|
84
|
+
exports.getDefaultFetch = getDefaultFetch;
|
|
83
85
|
exports.suppressExperimentalWarningsSync = suppressExperimentalWarningsSync;
|
|
84
86
|
// Use effectively-disabled keep-alive so short-lived CLI processes do not stay
|
|
85
87
|
// open waiting on idle sockets. Undici requires positive values, so we use 1ms.
|
|
@@ -87,41 +89,76 @@ var keepAliveOptions = {
|
|
|
87
89
|
keepAliveTimeout: 1,
|
|
88
90
|
keepAliveMaxTimeout: 1,
|
|
89
91
|
};
|
|
90
|
-
var
|
|
91
|
-
var
|
|
92
|
-
|
|
92
|
+
var defaultTransport;
|
|
93
|
+
var defaultTransportPromise;
|
|
94
|
+
/**
|
|
95
|
+
* The default dispatcher and its paired `fetch`, as a single value so the two
|
|
96
|
+
* are always read consistently. Resolves to `undefined` outside Node (browser/
|
|
97
|
+
* edge), where callers use the global `fetch` with no dispatcher.
|
|
98
|
+
*/
|
|
99
|
+
function getDefaultTransport() {
|
|
93
100
|
return __awaiter(this, void 0, void 0, function () {
|
|
94
101
|
return __generator(this, function (_a) {
|
|
95
|
-
if (
|
|
96
|
-
return [2 /*return*/,
|
|
102
|
+
if (defaultTransport) {
|
|
103
|
+
return [2 /*return*/, defaultTransport];
|
|
97
104
|
}
|
|
98
|
-
if (!
|
|
99
|
-
|
|
100
|
-
.then(function (
|
|
101
|
-
|
|
102
|
-
return
|
|
105
|
+
if (!defaultTransportPromise) {
|
|
106
|
+
defaultTransportPromise = createDefaultTransport()
|
|
107
|
+
.then(function (transport) {
|
|
108
|
+
defaultTransport = transport;
|
|
109
|
+
return transport;
|
|
103
110
|
})
|
|
104
111
|
.catch(function (error) {
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
defaultTransport = undefined;
|
|
113
|
+
defaultTransportPromise = undefined;
|
|
107
114
|
throw error;
|
|
108
115
|
});
|
|
109
116
|
}
|
|
110
|
-
return [2 /*return*/,
|
|
117
|
+
return [2 /*return*/, defaultTransportPromise];
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function getDefaultDispatcher() {
|
|
122
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
123
|
+
var _a;
|
|
124
|
+
return __generator(this, function (_b) {
|
|
125
|
+
switch (_b.label) {
|
|
126
|
+
case 0: return [4 /*yield*/, getDefaultTransport()];
|
|
127
|
+
case 1: return [2 /*return*/, (_a = (_b.sent())) === null || _a === void 0 ? void 0 : _a.dispatcher];
|
|
128
|
+
}
|
|
111
129
|
});
|
|
112
130
|
});
|
|
113
131
|
}
|
|
114
132
|
function resetDefaultDispatcherForTests() {
|
|
115
|
-
|
|
116
|
-
|
|
133
|
+
defaultTransport = undefined;
|
|
134
|
+
defaultTransportPromise = undefined;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The `fetch` implementation that must be used with the default dispatcher.
|
|
138
|
+
* Returns undici's own `fetch` on the full-undici Node path, or `undefined`
|
|
139
|
+
* (meaning: use the global `fetch`) in the browser/edge/Bun paths.
|
|
140
|
+
*
|
|
141
|
+
* Node's global `fetch` is backed by whatever undici version ships inside that
|
|
142
|
+
* Node release (6.x on Node 22 … 8.x on Node 26). Our dispatcher — and its
|
|
143
|
+
* `decompress` interceptor — comes from the npm `undici` package, which is a
|
|
144
|
+
* different version. Handing an npm-undici dispatcher to a mismatched built-in
|
|
145
|
+
* client makes gzip responses fail mid-stream with `terminated`. Sourcing
|
|
146
|
+
* `fetch` from the same npm `undici` keeps the whole request path on one
|
|
147
|
+
* version and removes the split.
|
|
148
|
+
*
|
|
149
|
+
* Only meaningful after {@link getDefaultTransport} has resolved, which is the
|
|
150
|
+
* one place that populates it.
|
|
151
|
+
*/
|
|
152
|
+
function getDefaultFetch() {
|
|
153
|
+
return defaultTransport === null || defaultTransport === void 0 ? void 0 : defaultTransport.fetch;
|
|
117
154
|
}
|
|
118
155
|
function isNodeEnvironment() {
|
|
119
156
|
var _a;
|
|
120
157
|
return typeof process !== 'undefined' && typeof ((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node) === 'string';
|
|
121
158
|
}
|
|
122
|
-
function
|
|
159
|
+
function createDefaultTransport() {
|
|
123
160
|
return __awaiter(this, void 0, void 0, function () {
|
|
124
|
-
var _a, EnvHttpProxyAgent, interceptors, decompress;
|
|
161
|
+
var _a, EnvHttpProxyAgent, interceptors, undiciFetch, agent, decompress;
|
|
125
162
|
return __generator(this, function (_b) {
|
|
126
163
|
switch (_b.label) {
|
|
127
164
|
case 0:
|
|
@@ -130,9 +167,27 @@ function createDefaultDispatcher() {
|
|
|
130
167
|
}
|
|
131
168
|
return [4 /*yield*/, Promise.resolve().then(function () { return __importStar(require('undici')); })];
|
|
132
169
|
case 1:
|
|
133
|
-
_a = _b.sent(), EnvHttpProxyAgent = _a.EnvHttpProxyAgent, interceptors = _a.interceptors;
|
|
170
|
+
_a = _b.sent(), EnvHttpProxyAgent = _a.EnvHttpProxyAgent, interceptors = _a.interceptors, undiciFetch = _a.fetch;
|
|
171
|
+
agent = new EnvHttpProxyAgent(keepAliveOptions);
|
|
172
|
+
// Some runtimes report `process.versions.node` (so `isNodeEnvironment()`
|
|
173
|
+
// passes) but ship only a partial undici: `interceptors.decompress` is
|
|
174
|
+
// absent and dispatchers have no `.compose`. Bun is the common case. There
|
|
175
|
+
// the proxy agent alone is enough — Bun's `fetch` decompresses
|
|
176
|
+
// gzip/deflate/br/zstd natively — so skip the interceptor instead of
|
|
177
|
+
// crashing on the missing API. Optional chaining also guards a runtime that
|
|
178
|
+
// omits the `interceptors` export entirely.
|
|
179
|
+
if (typeof (interceptors === null || interceptors === void 0 ? void 0 : interceptors.decompress) !== 'function') {
|
|
180
|
+
// Bun: pair the agent with the global `fetch` (undefined), which
|
|
181
|
+
// decompresses natively.
|
|
182
|
+
return [2 /*return*/, { dispatcher: agent, fetch: undefined }];
|
|
183
|
+
}
|
|
134
184
|
decompress = suppressExperimentalWarningsSync(function () { return interceptors.decompress(); });
|
|
135
|
-
|
|
185
|
+
// Pair undici's own `fetch` with this dispatcher so the request client and
|
|
186
|
+
// the dispatcher stay on one undici version (see `getDefaultFetch`). The
|
|
187
|
+
// global `fetch` is backed by a different, Node-bundled undici; mixing the
|
|
188
|
+
// two makes the decompress interceptor terminate gzip responses on some
|
|
189
|
+
// Node versions.
|
|
190
|
+
return [2 /*return*/, { dispatcher: agent.compose(decompress), fetch: undiciFetch }];
|
|
136
191
|
}
|
|
137
192
|
});
|
|
138
193
|
});
|
|
@@ -1,5 +1,72 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
13
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
14
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
15
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
16
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
17
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
18
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
22
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
23
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
24
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
25
|
+
function step(op) {
|
|
26
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
27
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
28
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
29
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
30
|
+
switch (op[0]) {
|
|
31
|
+
case 0: case 1: t = op; break;
|
|
32
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
33
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
34
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
35
|
+
default:
|
|
36
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
37
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
38
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
39
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
40
|
+
if (t[2]) _.ops.pop();
|
|
41
|
+
_.trys.pop(); continue;
|
|
42
|
+
}
|
|
43
|
+
op = body.call(thisArg, _);
|
|
44
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
45
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
46
|
+
}
|
|
47
|
+
};
|
|
1
48
|
import { setupServer } from 'msw/node';
|
|
2
|
-
import { afterAll, afterEach, beforeAll } from 'vitest';
|
|
49
|
+
import { afterAll, afterEach, beforeAll, vi } from 'vitest';
|
|
50
|
+
// In production the transport uses undici's own `fetch` (paired with the
|
|
51
|
+
// dispatcher) so the request client and dispatcher stay on one undici version.
|
|
52
|
+
// MSW intercepts the *global* `fetch`, not that separate undici instance, so
|
|
53
|
+
// force the transport onto the global `fetch` for every test in the suite;
|
|
54
|
+
// MSW then intercepts requests as usual. The real undici transport is covered
|
|
55
|
+
// directly by `http-dispatcher.test.ts` and `fetch-with-retry.test.ts`, which
|
|
56
|
+
// opt out of this seam.
|
|
57
|
+
vi.mock('../transport/http-dispatcher', function (importOriginal) { return __awaiter(void 0, void 0, void 0, function () {
|
|
58
|
+
var actual;
|
|
59
|
+
return __generator(this, function (_a) {
|
|
60
|
+
switch (_a.label) {
|
|
61
|
+
case 0: return [4 /*yield*/, importOriginal()];
|
|
62
|
+
case 1:
|
|
63
|
+
actual = _a.sent();
|
|
64
|
+
return [2 /*return*/, __assign(__assign({}, actual), { getDefaultTransport: vi.fn(function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
|
65
|
+
return [2 /*return*/, undefined];
|
|
66
|
+
}); }); }) })];
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}); });
|
|
3
70
|
// Create MSW server instance
|
|
4
71
|
export var server = setupServer();
|
|
5
72
|
// Start server before all tests
|
|
@@ -48,7 +48,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
48
48
|
import { TwistRequestError } from '../types/errors.js';
|
|
49
49
|
import { camelCaseKeys } from '../utils/case-conversion.js';
|
|
50
50
|
import { transformTimestamps } from '../utils/timestamp-conversion.js';
|
|
51
|
-
import {
|
|
51
|
+
import { getDefaultTransport } from './http-dispatcher.js';
|
|
52
52
|
export function fetchWithRetry(url_1, options_1) {
|
|
53
53
|
return __awaiter(this, arguments, void 0, function (url, options, maxRetries, customFetch) {
|
|
54
54
|
var lastError, attempt, clearTimeoutFn, requestSignal, timeoutResult, response, _a, responseText, responseData, camelCased, transformed, error_1, delay;
|
|
@@ -134,23 +134,29 @@ export function fetchWithRetry(url_1, options_1) {
|
|
|
134
134
|
}
|
|
135
135
|
function fetchWithDefaultTransport(url, options, signal) {
|
|
136
136
|
return __awaiter(this, void 0, void 0, function () {
|
|
137
|
-
var
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
var transport, fetchImpl, response, _a;
|
|
138
|
+
var _b;
|
|
139
|
+
return __generator(this, function (_c) {
|
|
140
|
+
switch (_c.label) {
|
|
141
|
+
case 0: return [4 /*yield*/, getDefaultTransport()
|
|
142
|
+
// undici's `fetch` and the global `fetch` are the same function at runtime
|
|
143
|
+
// but carry different (undici vs DOM) `RequestInit`/`Response` types. Call
|
|
144
|
+
// through the global signature, which matches the global-typed `options`.
|
|
145
|
+
];
|
|
141
146
|
case 1:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
147
|
+
transport = _c.sent();
|
|
148
|
+
fetchImpl = ((_b = transport === null || transport === void 0 ? void 0 : transport.fetch) !== null && _b !== void 0 ? _b : fetch);
|
|
149
|
+
if (!(transport === null || transport === void 0 ? void 0 : transport.dispatcher)) return [3 /*break*/, 3];
|
|
150
|
+
return [4 /*yield*/, fetchImpl(url, __assign(__assign({}, options), { signal: signal,
|
|
145
151
|
// @ts-expect-error - dispatcher is valid for Node.js fetch but not in TS types
|
|
146
|
-
dispatcher: dispatcher }))];
|
|
152
|
+
dispatcher: transport.dispatcher }))];
|
|
147
153
|
case 2:
|
|
148
|
-
_a =
|
|
154
|
+
_a = _c.sent();
|
|
149
155
|
return [3 /*break*/, 5];
|
|
150
|
-
case 3: return [4 /*yield*/,
|
|
156
|
+
case 3: return [4 /*yield*/, fetchImpl(url, __assign(__assign({}, options), { signal: signal }))];
|
|
151
157
|
case 4:
|
|
152
|
-
_a =
|
|
153
|
-
|
|
158
|
+
_a = _c.sent();
|
|
159
|
+
_c.label = 5;
|
|
154
160
|
case 5:
|
|
155
161
|
response = _a;
|
|
156
162
|
return [2 /*return*/, convertResponseToCustomFetch(response)];
|
|
@@ -49,59 +49,105 @@ var keepAliveOptions = {
|
|
|
49
49
|
keepAliveTimeout: 1,
|
|
50
50
|
keepAliveMaxTimeout: 1,
|
|
51
51
|
};
|
|
52
|
-
var
|
|
53
|
-
var
|
|
54
|
-
|
|
52
|
+
var defaultTransport;
|
|
53
|
+
var defaultTransportPromise;
|
|
54
|
+
/**
|
|
55
|
+
* The default dispatcher and its paired `fetch`, as a single value so the two
|
|
56
|
+
* are always read consistently. Resolves to `undefined` outside Node (browser/
|
|
57
|
+
* edge), where callers use the global `fetch` with no dispatcher.
|
|
58
|
+
*/
|
|
59
|
+
export function getDefaultTransport() {
|
|
55
60
|
return __awaiter(this, void 0, void 0, function () {
|
|
56
61
|
return __generator(this, function (_a) {
|
|
57
|
-
if (
|
|
58
|
-
return [2 /*return*/,
|
|
62
|
+
if (defaultTransport) {
|
|
63
|
+
return [2 /*return*/, defaultTransport];
|
|
59
64
|
}
|
|
60
|
-
if (!
|
|
61
|
-
|
|
62
|
-
.then(function (
|
|
63
|
-
|
|
64
|
-
return
|
|
65
|
+
if (!defaultTransportPromise) {
|
|
66
|
+
defaultTransportPromise = createDefaultTransport()
|
|
67
|
+
.then(function (transport) {
|
|
68
|
+
defaultTransport = transport;
|
|
69
|
+
return transport;
|
|
65
70
|
})
|
|
66
71
|
.catch(function (error) {
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
defaultTransport = undefined;
|
|
73
|
+
defaultTransportPromise = undefined;
|
|
69
74
|
throw error;
|
|
70
75
|
});
|
|
71
76
|
}
|
|
72
|
-
return [2 /*return*/,
|
|
77
|
+
return [2 /*return*/, defaultTransportPromise];
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
export function getDefaultDispatcher() {
|
|
82
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
83
|
+
var _a;
|
|
84
|
+
return __generator(this, function (_b) {
|
|
85
|
+
switch (_b.label) {
|
|
86
|
+
case 0: return [4 /*yield*/, getDefaultTransport()];
|
|
87
|
+
case 1: return [2 /*return*/, (_a = (_b.sent())) === null || _a === void 0 ? void 0 : _a.dispatcher];
|
|
88
|
+
}
|
|
73
89
|
});
|
|
74
90
|
});
|
|
75
91
|
}
|
|
76
92
|
export function resetDefaultDispatcherForTests() {
|
|
77
|
-
|
|
78
|
-
|
|
93
|
+
defaultTransport = undefined;
|
|
94
|
+
defaultTransportPromise = undefined;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The `fetch` implementation that must be used with the default dispatcher.
|
|
98
|
+
* Returns undici's own `fetch` on the full-undici Node path, or `undefined`
|
|
99
|
+
* (meaning: use the global `fetch`) in the browser/edge/Bun paths.
|
|
100
|
+
*
|
|
101
|
+
* Node's global `fetch` is backed by whatever undici version ships inside that
|
|
102
|
+
* Node release (6.x on Node 22 … 8.x on Node 26). Our dispatcher — and its
|
|
103
|
+
* `decompress` interceptor — comes from the npm `undici` package, which is a
|
|
104
|
+
* different version. Handing an npm-undici dispatcher to a mismatched built-in
|
|
105
|
+
* client makes gzip responses fail mid-stream with `terminated`. Sourcing
|
|
106
|
+
* `fetch` from the same npm `undici` keeps the whole request path on one
|
|
107
|
+
* version and removes the split.
|
|
108
|
+
*
|
|
109
|
+
* Only meaningful after {@link getDefaultTransport} has resolved, which is the
|
|
110
|
+
* one place that populates it.
|
|
111
|
+
*/
|
|
112
|
+
export function getDefaultFetch() {
|
|
113
|
+
return defaultTransport === null || defaultTransport === void 0 ? void 0 : defaultTransport.fetch;
|
|
79
114
|
}
|
|
80
115
|
function isNodeEnvironment() {
|
|
81
116
|
var _a;
|
|
82
117
|
return typeof process !== 'undefined' && typeof ((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node) === 'string';
|
|
83
118
|
}
|
|
84
|
-
function
|
|
119
|
+
function createDefaultTransport() {
|
|
85
120
|
return __awaiter(this, void 0, void 0, function () {
|
|
86
|
-
var _a, EnvHttpProxyAgent, interceptors, decompress;
|
|
121
|
+
var _a, EnvHttpProxyAgent, interceptors, undiciFetch, agent, decompress;
|
|
87
122
|
return __generator(this, function (_b) {
|
|
88
123
|
switch (_b.label) {
|
|
89
124
|
case 0:
|
|
90
125
|
if (!isNodeEnvironment()) {
|
|
91
126
|
return [2 /*return*/, undefined];
|
|
92
127
|
}
|
|
93
|
-
return [4 /*yield*/, import('undici')
|
|
94
|
-
// Compose the response-decompression interceptor so gzip/deflate/br/zstd
|
|
95
|
-
// bodies are decoded before consumers parse them. Required on Node 24+:
|
|
96
|
-
// attaching any custom dispatcher to the global `fetch` strips the
|
|
97
|
-
// `content-encoding` header but does not actually decompress the body,
|
|
98
|
-
// so callers receive raw gzipped bytes and `JSON.parse` fails.
|
|
99
|
-
// See https://github.com/Doist/todoist-cli/issues/318.
|
|
100
|
-
];
|
|
128
|
+
return [4 /*yield*/, import('undici')];
|
|
101
129
|
case 1:
|
|
102
|
-
_a = _b.sent(), EnvHttpProxyAgent = _a.EnvHttpProxyAgent, interceptors = _a.interceptors;
|
|
130
|
+
_a = _b.sent(), EnvHttpProxyAgent = _a.EnvHttpProxyAgent, interceptors = _a.interceptors, undiciFetch = _a.fetch;
|
|
131
|
+
agent = new EnvHttpProxyAgent(keepAliveOptions);
|
|
132
|
+
// Some runtimes report `process.versions.node` (so `isNodeEnvironment()`
|
|
133
|
+
// passes) but ship only a partial undici: `interceptors.decompress` is
|
|
134
|
+
// absent and dispatchers have no `.compose`. Bun is the common case. There
|
|
135
|
+
// the proxy agent alone is enough — Bun's `fetch` decompresses
|
|
136
|
+
// gzip/deflate/br/zstd natively — so skip the interceptor instead of
|
|
137
|
+
// crashing on the missing API. Optional chaining also guards a runtime that
|
|
138
|
+
// omits the `interceptors` export entirely.
|
|
139
|
+
if (typeof (interceptors === null || interceptors === void 0 ? void 0 : interceptors.decompress) !== 'function') {
|
|
140
|
+
// Bun: pair the agent with the global `fetch` (undefined), which
|
|
141
|
+
// decompresses natively.
|
|
142
|
+
return [2 /*return*/, { dispatcher: agent, fetch: undefined }];
|
|
143
|
+
}
|
|
103
144
|
decompress = suppressExperimentalWarningsSync(function () { return interceptors.decompress(); });
|
|
104
|
-
|
|
145
|
+
// Pair undici's own `fetch` with this dispatcher so the request client and
|
|
146
|
+
// the dispatcher stay on one undici version (see `getDefaultFetch`). The
|
|
147
|
+
// global `fetch` is backed by a different, Node-bundled undici; mixing the
|
|
148
|
+
// two makes the decompress interceptor terminate gzip responses on some
|
|
149
|
+
// Node versions.
|
|
150
|
+
return [2 /*return*/, { dispatcher: agent.compose(decompress), fetch: undiciFetch }];
|
|
105
151
|
}
|
|
106
152
|
});
|
|
107
153
|
});
|
|
@@ -1,4 +1,40 @@
|
|
|
1
1
|
import type { Dispatcher } from 'undici';
|
|
2
|
+
type UndiciFetch = typeof import('undici').fetch;
|
|
3
|
+
/**
|
|
4
|
+
* A dispatcher and the `fetch` that must be used with it. `fetch` is undici's
|
|
5
|
+
* own `fetch` on the full-undici Node path, or `undefined` (meaning: use the
|
|
6
|
+
* global `fetch`) on the Bun path. The two are cached together so callers can
|
|
7
|
+
* never observe a dispatcher paired with a mismatched `fetch` — see
|
|
8
|
+
* {@link getDefaultFetch} for why the pairing matters.
|
|
9
|
+
*/
|
|
10
|
+
type DefaultTransport = {
|
|
11
|
+
dispatcher: Dispatcher;
|
|
12
|
+
fetch: UndiciFetch | undefined;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The default dispatcher and its paired `fetch`, as a single value so the two
|
|
16
|
+
* are always read consistently. Resolves to `undefined` outside Node (browser/
|
|
17
|
+
* edge), where callers use the global `fetch` with no dispatcher.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getDefaultTransport(): Promise<DefaultTransport | undefined>;
|
|
2
20
|
export declare function getDefaultDispatcher(): Promise<Dispatcher | undefined>;
|
|
3
21
|
export declare function resetDefaultDispatcherForTests(): void;
|
|
22
|
+
/**
|
|
23
|
+
* The `fetch` implementation that must be used with the default dispatcher.
|
|
24
|
+
* Returns undici's own `fetch` on the full-undici Node path, or `undefined`
|
|
25
|
+
* (meaning: use the global `fetch`) in the browser/edge/Bun paths.
|
|
26
|
+
*
|
|
27
|
+
* Node's global `fetch` is backed by whatever undici version ships inside that
|
|
28
|
+
* Node release (6.x on Node 22 … 8.x on Node 26). Our dispatcher — and its
|
|
29
|
+
* `decompress` interceptor — comes from the npm `undici` package, which is a
|
|
30
|
+
* different version. Handing an npm-undici dispatcher to a mismatched built-in
|
|
31
|
+
* client makes gzip responses fail mid-stream with `terminated`. Sourcing
|
|
32
|
+
* `fetch` from the same npm `undici` keeps the whole request path on one
|
|
33
|
+
* version and removes the split.
|
|
34
|
+
*
|
|
35
|
+
* Only meaningful after {@link getDefaultTransport} has resolved, which is the
|
|
36
|
+
* one place that populates it.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getDefaultFetch(): UndiciFetch | undefined;
|
|
4
39
|
export declare function suppressExperimentalWarningsSync<T>(fn: () => T): T;
|
|
40
|
+
export {};
|