|
|
@@ -0,0 +1,167 @@ |
|
|
|
// https://unpkg.com/isomorphic-git@1.7.2/http/web/index.js |
|
|
|
/** |
|
|
|
* @typedef {Object} GitProgressEvent |
|
|
|
* @property {string} phase |
|
|
|
* @property {number} loaded |
|
|
|
* @property {number} total |
|
|
|
*/ |
|
|
|
|
|
|
|
/** |
|
|
|
* @callback ProgressCallback |
|
|
|
* @param {GitProgressEvent} progress |
|
|
|
* @returns {void | Promise<void>} |
|
|
|
*/ |
|
|
|
|
|
|
|
/** |
|
|
|
* @typedef {Object} GitHttpRequest |
|
|
|
* @property {string} url - The URL to request |
|
|
|
* @property {string} [method='GET'] - The HTTP method to use |
|
|
|
* @property {Object<string, string>} [headers={}] - Headers to include in the HTTP request |
|
|
|
* @property {AsyncIterableIterator<Uint8Array>} [body] - An async iterator of Uint8Arrays that make up the body of POST requests |
|
|
|
* @property {ProgressCallback} [onProgress] - Reserved for future use (emitting `GitProgressEvent`s) |
|
|
|
* @property {object} [signal] - Reserved for future use (canceling a request) |
|
|
|
*/ |
|
|
|
|
|
|
|
/** |
|
|
|
* @typedef {Object} GitHttpResponse |
|
|
|
* @property {string} url - The final URL that was fetched after any redirects |
|
|
|
* @property {string} [method] - The HTTP method that was used |
|
|
|
* @property {Object<string, string>} [headers] - HTTP response headers |
|
|
|
* @property {AsyncIterableIterator<Uint8Array>} [body] - An async iterator of Uint8Arrays that make up the body of the response |
|
|
|
* @property {number} statusCode - The HTTP status code |
|
|
|
* @property {string} statusMessage - The HTTP status message |
|
|
|
*/ |
|
|
|
|
|
|
|
/** |
|
|
|
* @callback HttpFetch |
|
|
|
* @param {GitHttpRequest} request |
|
|
|
* @returns {Promise<GitHttpResponse>} |
|
|
|
*/ |
|
|
|
|
|
|
|
/** |
|
|
|
* @typedef {Object} HttpClient |
|
|
|
* @property {HttpFetch} request |
|
|
|
*/ |
|
|
|
|
|
|
|
// Convert a value to an Async Iterator |
|
|
|
// This will be easier with async generator functions. |
|
|
|
function fromValue(value) { |
|
|
|
let queue = [value]; |
|
|
|
return { |
|
|
|
next() { |
|
|
|
return Promise.resolve({ done: queue.length === 0, value: queue.pop() }) |
|
|
|
}, |
|
|
|
return() { |
|
|
|
queue = []; |
|
|
|
return {} |
|
|
|
}, |
|
|
|
[Symbol.asyncIterator]() { |
|
|
|
return this |
|
|
|
}, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function getIterator(iterable) { |
|
|
|
if (iterable[Symbol.asyncIterator]) { |
|
|
|
return iterable[Symbol.asyncIterator]() |
|
|
|
} |
|
|
|
if (iterable[Symbol.iterator]) { |
|
|
|
return iterable[Symbol.iterator]() |
|
|
|
} |
|
|
|
if (iterable.next) { |
|
|
|
return iterable |
|
|
|
} |
|
|
|
return fromValue(iterable) |
|
|
|
} |
|
|
|
|
|
|
|
// Currently 'for await' upsets my linters. |
|
|
|
async function forAwait(iterable, cb) { |
|
|
|
const iter = getIterator(iterable); |
|
|
|
while (true) { |
|
|
|
const { value, done } = await iter.next(); |
|
|
|
if (value) await cb(value); |
|
|
|
if (done) break |
|
|
|
} |
|
|
|
if (iter.return) iter.return(); |
|
|
|
} |
|
|
|
|
|
|
|
async function collect(iterable) { |
|
|
|
let size = 0; |
|
|
|
const buffers = []; |
|
|
|
// This will be easier once `for await ... of` loops are available. |
|
|
|
await forAwait(iterable, value => { |
|
|
|
buffers.push(value); |
|
|
|
size += value.byteLength; |
|
|
|
}); |
|
|
|
const result = new Uint8Array(size); |
|
|
|
let nextIndex = 0; |
|
|
|
for (const buffer of buffers) { |
|
|
|
result.set(buffer, nextIndex); |
|
|
|
nextIndex += buffer.byteLength; |
|
|
|
} |
|
|
|
return result |
|
|
|
} |
|
|
|
|
|
|
|
// Convert a web ReadableStream (not Node stream!) to an Async Iterator |
|
|
|
// adapted from https://jakearchibald.com/2017/async-iterators-and-generators/ |
|
|
|
function fromStream(stream) { |
|
|
|
// Use native async iteration if it's available. |
|
|
|
if (stream[Symbol.asyncIterator]) return stream |
|
|
|
const reader = stream.getReader(); |
|
|
|
return { |
|
|
|
next() { |
|
|
|
return reader.read() |
|
|
|
}, |
|
|
|
return() { |
|
|
|
reader.releaseLock(); |
|
|
|
return {} |
|
|
|
}, |
|
|
|
[Symbol.asyncIterator]() { |
|
|
|
return this |
|
|
|
}, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* eslint-env browser */ |
|
|
|
|
|
|
|
/** |
|
|
|
* HttpClient |
|
|
|
* |
|
|
|
* @param {GitHttpRequest} request |
|
|
|
* @returns {Promise<GitHttpResponse>} |
|
|
|
*/ |
|
|
|
async function request({ |
|
|
|
onProgress, |
|
|
|
url, |
|
|
|
method = 'GET', |
|
|
|
headers = {}, |
|
|
|
body, |
|
|
|
}) { |
|
|
|
// streaming uploads aren't possible yet in the browser |
|
|
|
if (body) { |
|
|
|
body = await collect(body); |
|
|
|
} |
|
|
|
const res = await fetch(url, { method, headers, body }); |
|
|
|
const iter = |
|
|
|
res.body && res.body.getReader |
|
|
|
? fromStream(res.body) |
|
|
|
: [new Uint8Array(await res.arrayBuffer())]; |
|
|
|
// convert Header object to ordinary JSON |
|
|
|
headers = {}; |
|
|
|
for (const [key, value] of res.headers.entries()) { |
|
|
|
headers[key] = value; |
|
|
|
} |
|
|
|
return { |
|
|
|
url: res.url, |
|
|
|
method: res.method, |
|
|
|
statusCode: res.status, |
|
|
|
statusMessage: res.statusText, |
|
|
|
body: iter, |
|
|
|
headers: headers, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var index = { request }; |
|
|
|
|
|
|
|
//export default index; |
|
|
|
//export { request }; |