@forge/api 2.17.0 → 2.18.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 +12 -0
- package/out/api/fetch.d.ts +2 -1
- package/out/api/fetch.d.ts.map +1 -1
- package/out/api/fetch.js +84 -14
- package/out/api/runtime.d.ts +6 -5
- package/out/api/runtime.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/out/api/fetch.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RequestInfo, RequestInit, Response } from 'node-fetch';
|
|
1
|
+
import { RequestInfo, RequestInit, Response, Headers } from 'node-fetch';
|
|
2
2
|
import { FetchAPI } from '..';
|
|
3
3
|
declare type FetchFunction = (url: RequestInfo, options?: RequestInit) => Promise<Response>;
|
|
4
4
|
declare type ProxyUrlProvider = 'app' | 'user' | 'none';
|
|
@@ -10,6 +10,7 @@ declare type ProxyFetchArgs = {
|
|
|
10
10
|
} | {
|
|
11
11
|
type: 'egress';
|
|
12
12
|
};
|
|
13
|
+
export declare const getRedirectUrl: (responseHeaders: Headers, original: string, isFromEgress: boolean) => string;
|
|
13
14
|
export declare const createProxyFetch: (args: ProxyFetchArgs) => FetchFunction;
|
|
14
15
|
export declare function getNodeRuntimeAPI(): FetchAPI;
|
|
15
16
|
export declare function getSandboxRuntimeAPI(api: any): FetchAPI;
|
package/out/api/fetch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/api/fetch.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAW,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/api/fetch.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAW,OAAO,EAAE,MAAM,YAAY,CAAC;AAEzF,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAS9B,aAAK,aAAa,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AACpF,aAAK,gBAAgB,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAChD,aAAK,cAAc,GAAG,MAAM,GAAG,YAAY,GAAG,UAAU,GAAG,WAAW,CAAC;AA4BvE,aAAK,cAAc,GAAG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAC;AAsE/G,eAAO,MAAM,cAAc,oBAAqB,OAAO,YAAY,MAAM,gBAAgB,OAAO,KAAG,MAQlG,CAAC;AAyEF,eAAO,MAAM,gBAAgB,SAAU,cAAc,KAAG,aAerD,CAAC;AAOJ,wBAAgB,iBAAiB,IAAI,QAAQ,CAoB5C;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,GAAG,GAAG,QAAQ,CAEvD"}
|
package/out/api/fetch.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getSandboxRuntimeAPI = exports.getNodeRuntimeAPI = exports.createProxyFetch = void 0;
|
|
3
|
+
exports.getSandboxRuntimeAPI = exports.getNodeRuntimeAPI = exports.createProxyFetch = exports.getRedirectUrl = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const node_fetch_1 = tslib_1.__importStar(require("node-fetch"));
|
|
6
6
|
const _1 = require(".");
|
|
@@ -32,46 +32,116 @@ const metricName = (args) => {
|
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
34
|
const createProxyRequest = ({ url: proxyUrl, token, host }, args, originalRequest) => {
|
|
35
|
-
let
|
|
35
|
+
let proxyEndpoint;
|
|
36
36
|
switch (args.type) {
|
|
37
37
|
case 'egress':
|
|
38
|
-
|
|
39
|
-
const url = new URL(originalRequest.url);
|
|
40
|
-
proxyRequest.headers.set('Forge-Proxy-Target-Relative', `${url.pathname}${url.search}`);
|
|
41
|
-
proxyRequest.headers.set('Forge-Proxy-Target-Host', url.hostname);
|
|
38
|
+
proxyEndpoint = `${proxyUrl}/egress`;
|
|
42
39
|
break;
|
|
43
40
|
case 'fpp':
|
|
44
|
-
|
|
45
|
-
proxyRequest.headers.set('Forge-Proxy-Target-Relative', originalRequest.url);
|
|
41
|
+
proxyEndpoint = `${proxyUrl}/fpp/provider/${args.provider}/remote/${args.remote}`;
|
|
46
42
|
break;
|
|
47
43
|
}
|
|
44
|
+
const tempRequest = new node_fetch_1.Request(proxyEndpoint, originalRequest);
|
|
45
|
+
const proxyRequest = new node_fetch_1.Request(tempRequest, { redirect: 'manual' });
|
|
46
|
+
proxyRequest.headers.set('Forge-Proxy-Target', originalRequest.url);
|
|
48
47
|
proxyRequest.headers.set('Forge-Proxy-Authorization', `Bearer ${token}`);
|
|
49
48
|
if (host) {
|
|
50
49
|
proxyRequest.headers.set('Host', host);
|
|
51
50
|
}
|
|
52
51
|
return proxyRequest;
|
|
53
52
|
};
|
|
54
|
-
const
|
|
55
|
-
const { proxy, metrics } = (0, runtime_1.getRuntime)();
|
|
56
|
-
const request = new node_fetch_1.Request(url, options);
|
|
57
|
-
const proxyRequest = createProxyRequest(proxy, args, request);
|
|
53
|
+
const wrapWithOverheadMetric = async (fetch, metrics) => {
|
|
58
54
|
const requestStart = perf_hooks_1.performance.now();
|
|
59
|
-
const response = await
|
|
55
|
+
const response = await fetch;
|
|
60
56
|
const requestEnd = perf_hooks_1.performance.now();
|
|
61
57
|
const proxyUpstreamLatency = parseInt(response.headers.get(FORGE_PROXY_UPSTREAM_LATENCY_HEADER) || '');
|
|
62
58
|
if (proxyUpstreamLatency) {
|
|
63
59
|
metrics.timing('proxy-success-overhead').set(requestEnd - requestStart - proxyUpstreamLatency);
|
|
64
60
|
}
|
|
61
|
+
return response;
|
|
62
|
+
};
|
|
63
|
+
const getRedirectArgs = (args, response) => {
|
|
64
|
+
const proxyRelativeLocation = response.headers.get('forge-proxy-relative-location');
|
|
65
|
+
if (!proxyRelativeLocation) {
|
|
66
|
+
return { type: 'egress' };
|
|
67
|
+
}
|
|
68
|
+
return args;
|
|
69
|
+
};
|
|
70
|
+
const buildGetRequest = (url, originalRequest) => {
|
|
71
|
+
const request = new node_fetch_1.Request(url, { method: 'GET' });
|
|
72
|
+
for (const [name, values] of Array.from(originalRequest.headers.entries())) {
|
|
73
|
+
if (!name.toLowerCase().startsWith('content-')) {
|
|
74
|
+
request.headers.set(name, values);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return request;
|
|
78
|
+
};
|
|
79
|
+
const getLocation = (responseHeaders) => {
|
|
80
|
+
const location = responseHeaders.get('location');
|
|
81
|
+
if (!location) {
|
|
82
|
+
throw new errors_1.FetchError('Redirect location is empty');
|
|
83
|
+
}
|
|
84
|
+
return location;
|
|
85
|
+
};
|
|
86
|
+
const getRedirectUrl = (responseHeaders, original, isFromEgress) => {
|
|
87
|
+
if (!isFromEgress) {
|
|
88
|
+
return responseHeaders.get('forge-proxy-relative-location') || getLocation(responseHeaders);
|
|
89
|
+
}
|
|
90
|
+
return new URL(getLocation(responseHeaders), original).toString();
|
|
91
|
+
};
|
|
92
|
+
exports.getRedirectUrl = getRedirectUrl;
|
|
93
|
+
const buildRedirectedRequest = (response, originalRequest, isFromEgress) => {
|
|
94
|
+
const url = (0, exports.getRedirectUrl)(response.headers, originalRequest.url, isFromEgress);
|
|
95
|
+
if (response.status === 303) {
|
|
96
|
+
return buildGetRequest(url, originalRequest);
|
|
97
|
+
}
|
|
98
|
+
return new node_fetch_1.Request(url, originalRequest);
|
|
99
|
+
};
|
|
100
|
+
const fetchViaProxy = async ({ proxy, proxyFetchArgs, request, metrics, count = 0 }) => {
|
|
101
|
+
const REDIRECT_STATUS = [300, 301, 302, 303, 307, 308];
|
|
102
|
+
const MAX_REDIRECTS = 20;
|
|
103
|
+
if (count >= MAX_REDIRECTS) {
|
|
104
|
+
throw new errors_1.FetchError('Max redirects exceeded');
|
|
105
|
+
}
|
|
106
|
+
const proxyRequest = createProxyRequest(proxy, proxyFetchArgs, request);
|
|
107
|
+
if (request.redirect === 'manual' && count === 0) {
|
|
108
|
+
return wrapWithOverheadMetric((0, node_fetch_1.default)(proxyRequest), metrics);
|
|
109
|
+
}
|
|
110
|
+
const response = await wrapWithOverheadMetric((0, node_fetch_1.default)(proxyRequest), metrics);
|
|
111
|
+
if (REDIRECT_STATUS.includes(response.status)) {
|
|
112
|
+
const redirectedRequest = buildRedirectedRequest(response, request, proxyFetchArgs.type === 'egress');
|
|
113
|
+
const redirectedArgs = getRedirectArgs(proxyFetchArgs, response);
|
|
114
|
+
return fetchViaProxy({
|
|
115
|
+
proxy,
|
|
116
|
+
proxyFetchArgs: redirectedArgs,
|
|
117
|
+
request: redirectedRequest,
|
|
118
|
+
metrics,
|
|
119
|
+
count: count + 1
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return response;
|
|
123
|
+
};
|
|
124
|
+
const handleProxyResponseErrors = (response, requestUrl) => {
|
|
65
125
|
if (response.headers.has('forge-proxy-error')) {
|
|
66
126
|
const errorReason = response.headers.get('forge-proxy-error');
|
|
67
127
|
if (errorReason === 'NEEDS_AUTHENTICATION_ERR') {
|
|
68
128
|
throw new errors_1.NeedsAuthenticationError('Authentication Required', ATLASSIAN_TOKEN_SERVICE_KEY);
|
|
69
129
|
}
|
|
70
130
|
if (errorReason === 'BLOCKED_EGRESS') {
|
|
71
|
-
throw new errors_1.ExternalEndpointNotAllowedError(
|
|
131
|
+
throw new errors_1.ExternalEndpointNotAllowedError(requestUrl);
|
|
72
132
|
}
|
|
73
133
|
throw new errors_1.ProxyRequestError(response.status, response.headers.get('forge-proxy-error'));
|
|
74
134
|
}
|
|
135
|
+
};
|
|
136
|
+
const createProxyFetch = (args) => (0, runtime_1.wrapInMetrics)(metricName(args), async (url, options) => {
|
|
137
|
+
const { proxy, metrics } = (0, runtime_1.getRuntime)();
|
|
138
|
+
const response = await fetchViaProxy({
|
|
139
|
+
proxy,
|
|
140
|
+
proxyFetchArgs: args,
|
|
141
|
+
request: new node_fetch_1.Request(url, options),
|
|
142
|
+
metrics
|
|
143
|
+
});
|
|
144
|
+
handleProxyResponseErrors(response, url.toString());
|
|
75
145
|
return response;
|
|
76
146
|
}, { tags: { proxy: 'true' } });
|
|
77
147
|
exports.createProxyFetch = createProxyFetch;
|
package/out/api/runtime.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { Metrics, Tags } from '@forge/util/packages/metrics-interface';
|
|
2
|
+
export declare type ProxyInfo = {
|
|
3
|
+
token: string;
|
|
4
|
+
url: string;
|
|
5
|
+
host?: string;
|
|
6
|
+
};
|
|
2
7
|
export declare type ForgeRuntime = {
|
|
3
|
-
proxy:
|
|
4
|
-
token: string;
|
|
5
|
-
url: string;
|
|
6
|
-
host?: string;
|
|
7
|
-
};
|
|
8
|
+
proxy: ProxyInfo;
|
|
8
9
|
contextAri: string;
|
|
9
10
|
appContext: {
|
|
10
11
|
appId: string;
|
package/out/api/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/api/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,wCAAwC,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/api/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,wCAAwC,CAAC;AAU5E,oBAAY,SAAS,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,oBAAY,YAAY,GAAG;IACzB,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE;QACV,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,EAAE;QACb,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE;QACJ,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH,CAAC;AAEF,wBAAgB,UAAU,IAAI,YAAY,CAOzC;AAED,wBAAgB,aAAa,CAAC,IAAI,SAAS,OAAO,EAAE,EAAE,GAAG,EACvD,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,GAAG,CAAC,EACnC,EAAE,IAAI,EAAE,GAAE;IAAE,IAAI,CAAC,EAAE,IAAI,CAAA;CAAO,GAC7B,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,GAAG,CAAC,CAiBjC"}
|