git-documentdb
Version:
Offline-first database that syncs with Git
454 lines • 19.1 kB
JavaScript
"use strict";
/**
* GitDocumentDB
* Copyright (c) Hidekazu Kubota
*
* This source code is licensed under the Mozilla Public License Version 2.0
* found in the LICENSE file in the root directory of this source tree.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.push = exports.fetch = exports.checkFetch = exports.clone = exports.createCredentialCallback = exports.name = exports.type = void 0;
const fs_1 = __importDefault(require("fs"));
const isomorphic_git_1 = __importDefault(require("isomorphic-git"));
const tslog_1 = require("tslog");
const git_documentdb_remote_errors_1 = require("git-documentdb-remote-errors");
const node_1 = __importDefault(require("isomorphic-git/http/node"));
const const_1 = require("../const");
const utils_1 = require("../utils");
/**
* @public
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
exports.type = 'remote';
/**
* @public
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
exports.name = 'isomorphic-git';
/**
* Insert credential options for GitHub
*
* @throws {@link InvalidURLFormatError}
* @throws {@link InvalidRepositoryURLError}
*
* @internal
*/
function createCredentialForGitHub(options) {
if (!options.remoteUrl.match(/^https?:\/\//)) {
throw new git_documentdb_remote_errors_1.InvalidURLFormatError('http protocol required in createCredentialForGitHub');
}
const connection = options.connection;
if (!connection.personalAccessToken) {
return undefined;
}
const urlArray = options.remoteUrl.replace(/^https?:\/\//, '').split('/');
// github.com/account_name/repository_name
if (urlArray.length !== 3) {
throw new git_documentdb_remote_errors_1.InvalidRepositoryURLError(options.remoteUrl);
}
const credentials = connection.personalAccessToken !== undefined
? () => ({ username: connection.personalAccessToken })
: undefined;
return credentials;
}
/**
* Create credential options
*
* @throws {@link InvalidAuthenticationTypeError}
*
* @throws # Error from createCredentialForGitHub
* @throws - {@link InvalidURLFormatError}
* @throws - {@link InvalidRepositoryURLError}
*
* @internal
*/
function createCredentialCallback(options) {
var _a;
(_a = options.connection) !== null && _a !== void 0 ? _a : (options.connection = { type: 'none' });
if (options.connection.type === 'github') {
return createCredentialForGitHub(options);
}
else if (options.connection.type === 'none') {
return undefined;
}
// @ts-ignore
throw new git_documentdb_remote_errors_1.InvalidAuthenticationTypeError(options.connection.type);
}
exports.createCredentialCallback = createCredentialCallback;
/**
* Clone
*
* @throws {@link InvalidURLFormatError}
* @throws {@link NetworkError}
* @throws {@link HTTPError401AuthorizationRequired}
* @throws {@link HTTPError404NotFound}
* @throws {@link CannotConnectError}
*
* @throws # Errors from createCredentialForGitHub
* @throws - {@link HttpProtocolRequiredError}
* @throws - {@link InvalidRepositoryURLError}
*
* @throws # Errors from createCredential
* @throws - {@link InvalidAuthenticationTypeError}
*
* @internal
*/
// eslint-disable-next-line complexity
async function clone(workingDir, remoteOptions, remoteName, logger) {
var _a, _b;
logger !== null && logger !== void 0 ? logger : (logger = new tslog_1.Logger({
name: 'plugin-nodegit',
minLevel: 'trace',
displayDateTime: false,
displayFunctionName: false,
displayFilePath: 'hidden',
}));
logger.debug(`remote-isomorphic-git: clone: ${remoteOptions.remoteUrl}`);
remoteName !== null && remoteName !== void 0 ? remoteName : (remoteName = 'origin');
(_a = remoteOptions.retry) !== null && _a !== void 0 ? _a : (remoteOptions.retry = const_1.NETWORK_RETRY);
(_b = remoteOptions.retryInterval) !== null && _b !== void 0 ? _b : (remoteOptions.retryInterval = const_1.NETWORK_RETRY_INTERVAL);
const cloneOption = {
fs: fs_1.default,
dir: workingDir,
http: node_1.default,
url: remoteOptions.remoteUrl,
};
const cred = createCredentialCallback(remoteOptions);
if (cred) {
cloneOption.onAuth = cred;
}
for (let i = 0; i < remoteOptions.retry + 1; i++) {
// eslint-disable-next-line no-await-in-loop
const res = await isomorphic_git_1.default.clone(cloneOption).catch(err => err);
let error = '';
if (res instanceof Error) {
error = res.toString();
}
else {
break;
}
// if (error !== 'undefined') console.warn('connect fetch error: ' + error);
switch (true) {
case error.startsWith('UrlParseError:'):
case error.startsWith('Error: getaddrinfo ENOTFOUND'):
throw new git_documentdb_remote_errors_1.InvalidURLFormatError(error);
case error.startsWith('Error: connect EACCES'):
case error.startsWith('Error: connect ECONNREFUSED'):
// isomorphic-git throws this when network is limited.
if (i >= remoteOptions.retry) {
throw new git_documentdb_remote_errors_1.NetworkError(error);
}
break;
case error.startsWith('HttpError: HTTP Error: 401'):
throw new git_documentdb_remote_errors_1.HTTPError401AuthorizationRequired(error);
case error.startsWith('HttpError: HTTP Error: 404 Not Found'):
throw new git_documentdb_remote_errors_1.HTTPError404NotFound(error);
default:
if (i >= remoteOptions.retry) {
throw new git_documentdb_remote_errors_1.CannotConnectError(error);
}
}
// eslint-disable-next-line no-await-in-loop
await (0, utils_1.sleep)(remoteOptions.retryInterval);
}
// Add remote
// (default is 'origin')
if (remoteName !== 'origin') {
await isomorphic_git_1.default.addRemote({
fs: fs_1.default,
dir: workingDir,
remote: 'origin',
url: remoteOptions.remoteUrl,
});
}
await isomorphic_git_1.default.addRemote({
fs: fs_1.default,
dir: workingDir,
remote: remoteName,
url: remoteOptions.remoteUrl,
});
}
exports.clone = clone;
/**
* Check connection by FETCH
*
* @throws {@link InvalidGitRemoteError}
* @throws {@link InvalidURLFormatError}
* @throws {@link NetworkError}
* @throws {@link HTTPError401AuthorizationRequired}
* @throws {@link HTTPError404NotFound}
* @throws {@link CannotConnectError}
*
* @throws # Errors from createCredentialForGitHub
* @throws - {@link HttpProtocolRequiredError}
* @throws - {@link InvalidRepositoryURLError}
*
* @throws # Errors from createCredential
* @throws - {@link InvalidAuthenticationTypeError}
*
* @internal
*/
// eslint-disable-next-line complexity
async function checkFetch(workingDir, remoteOptions, remoteName, logger) {
var _a, _b;
logger !== null && logger !== void 0 ? logger : (logger = new tslog_1.Logger({
name: 'plugin-nodegit',
minLevel: 'trace',
displayDateTime: false,
displayFunctionName: false,
displayFilePath: 'hidden',
}));
remoteName !== null && remoteName !== void 0 ? remoteName : (remoteName = 'origin');
const urlOfRemote = await isomorphic_git_1.default.getConfig({
fs: fs_1.default,
dir: workingDir,
path: `remote.${remoteName}.url`,
});
if (urlOfRemote === undefined) {
throw new git_documentdb_remote_errors_1.InvalidGitRemoteError(`remote '${remoteName}' does not exist`);
}
(_a = remoteOptions.retry) !== null && _a !== void 0 ? _a : (remoteOptions.retry = const_1.NETWORK_RETRY);
(_b = remoteOptions.retryInterval) !== null && _b !== void 0 ? _b : (remoteOptions.retryInterval = const_1.NETWORK_RETRY_INTERVAL);
const checkOption = {
http: node_1.default,
url: remoteOptions.remoteUrl,
};
const cred = createCredentialCallback(remoteOptions);
if (cred) {
checkOption.onAuth = cred;
}
for (let i = 0; i < remoteOptions.retry + 1; i++) {
// eslint-disable-next-line no-await-in-loop
const res = await isomorphic_git_1.default.getRemoteInfo2(checkOption).catch(err => err);
let error = '';
if (res instanceof Error) {
error = res.toString();
}
else {
break;
}
switch (true) {
case error.startsWith('UrlParseError:'):
case error.startsWith('Error: getaddrinfo ENOTFOUND'):
throw new git_documentdb_remote_errors_1.InvalidURLFormatError(error);
case error.startsWith('Error: connect EACCES'):
case error.startsWith('Error: connect ECONNREFUSED'):
// isomorphic-git throws this when network is limited.
if (i >= remoteOptions.retry) {
throw new git_documentdb_remote_errors_1.NetworkError(error);
}
break;
case error.startsWith('HttpError: HTTP Error: 401'):
throw new git_documentdb_remote_errors_1.HTTPError401AuthorizationRequired(error);
case error.startsWith('HttpError: HTTP Error: 404 Not Found'):
throw new git_documentdb_remote_errors_1.HTTPError404NotFound(error);
default:
if (i >= remoteOptions.retry) {
throw new git_documentdb_remote_errors_1.CannotConnectError(error);
}
}
// eslint-disable-next-line no-await-in-loop
await (0, utils_1.sleep)(remoteOptions.retryInterval);
}
return true;
}
exports.checkFetch = checkFetch;
/**
* git fetch
*
* @throws {@link InvalidGitRemoteError}
* @throws {@link InvalidURLFormatError}
* @throws {@link NetworkError}
* @throws {@link HTTPError401AuthorizationRequired}
* @throws {@link HTTPError404NotFound}
* @throws {@link CannotConnectError}
*
* @throws # Errors from createCredentialForGitHub
* @throws - {@link HttpProtocolRequiredError}
* @throws - {@link InvalidRepositoryURLError}
*
* @throws # Errors from createCredential
* @throws - {@link InvalidAuthenticationTypeError}
*
* @internal
*/
// eslint-disable-next-line complexity
async function fetch(workingDir, remoteOptions, remoteName, localBranchName, remoteBranchName, logger) {
var _a, _b;
logger !== null && logger !== void 0 ? logger : (logger = new tslog_1.Logger({
name: 'plugin-nodegit',
minLevel: 'trace',
displayDateTime: false,
displayFunctionName: false,
displayFilePath: 'hidden',
}));
logger.debug(`remote-isomorphic-git: fetch: ${remoteOptions.remoteUrl}`);
remoteName !== null && remoteName !== void 0 ? remoteName : (remoteName = 'origin');
localBranchName !== null && localBranchName !== void 0 ? localBranchName : (localBranchName = 'main');
remoteBranchName !== null && remoteBranchName !== void 0 ? remoteBranchName : (remoteBranchName = 'main');
const urlOfRemote = await isomorphic_git_1.default.getConfig({
fs: fs_1.default,
dir: workingDir,
path: `remote.${remoteName}.url`,
});
if (urlOfRemote === undefined) {
throw new git_documentdb_remote_errors_1.InvalidGitRemoteError(`remote '${remoteName}' does not exist`);
}
(_a = remoteOptions.retry) !== null && _a !== void 0 ? _a : (remoteOptions.retry = const_1.NETWORK_RETRY);
(_b = remoteOptions.retryInterval) !== null && _b !== void 0 ? _b : (remoteOptions.retryInterval = const_1.NETWORK_RETRY_INTERVAL);
const fetchOption = {
fs: fs_1.default,
dir: workingDir,
http: node_1.default,
url: remoteOptions.remoteUrl,
remote: remoteName,
ref: localBranchName,
remoteRef: remoteBranchName,
};
const cred = createCredentialCallback(remoteOptions);
if (cred) {
fetchOption.onAuth = cred;
}
for (let i = 0; i < remoteOptions.retry + 1; i++) {
// eslint-disable-next-line no-await-in-loop
const res = await isomorphic_git_1.default.fetch(fetchOption).catch(err => err);
let error = '';
if (res instanceof Error) {
error = res.toString();
}
else {
break;
}
// if (error !== 'undefined') console.warn('connect fetch error: ' + error);
switch (true) {
case error.startsWith('NoRefspecError'):
throw new git_documentdb_remote_errors_1.InvalidGitRemoteError(error);
case error.startsWith('UrlParseError:'):
case error.startsWith('Error: getaddrinfo ENOTFOUND'):
throw new git_documentdb_remote_errors_1.InvalidURLFormatError(error);
case error.startsWith('Error: connect EACCES'):
case error.startsWith('Error: connect ECONNREFUSED'):
// isomorphic-git throws this when network is limited.
if (i >= remoteOptions.retry) {
throw new git_documentdb_remote_errors_1.NetworkError(error);
}
break;
case error.startsWith('HttpError: HTTP Error: 401'):
throw new git_documentdb_remote_errors_1.HTTPError401AuthorizationRequired(error);
case error.startsWith('HttpError: HTTP Error: 404 Not Found'):
throw new git_documentdb_remote_errors_1.HTTPError404NotFound(error);
default:
if (i >= remoteOptions.retry) {
throw new git_documentdb_remote_errors_1.CannotConnectError(error);
}
}
// eslint-disable-next-line no-await-in-loop
await (0, utils_1.sleep)(remoteOptions.retryInterval);
}
}
exports.fetch = fetch;
/**
* git push
*
* @throws {@link InvalidGitRemoteError}
* @throws {@link UnfetchedCommitExistsError}
* @throws {@link InvalidURLFormatError}
* @throws {@link NetworkError}
* @throws {@link HTTPError401AuthorizationRequired}
* @throws {@link HTTPError404NotFound}
* @throws {@link HTTPError403Forbidden}
* @throws {@link CannotConnectError}
*
* @throws # Errors from createCredentialForGitHub
* @throws - {@link InvalidURLFormatError}
* @throws - {@link InvalidRepositoryURLError}
*
* @throws # Errors from createCredential
* @throws - {@link InvalidAuthenticationTypeError}
*
* @internal
*/
// eslint-disable-next-line complexity
async function push(workingDir, remoteOptions, remoteName, localBranchName, remoteBranchName, logger) {
var _a, _b;
logger !== null && logger !== void 0 ? logger : (logger = new tslog_1.Logger({
name: 'plugin-nodegit',
minLevel: 'trace',
displayDateTime: false,
displayFunctionName: false,
displayFilePath: 'hidden',
}));
logger.debug(`remote-isomorphic-git: push: ${remoteOptions.remoteUrl}`);
remoteName !== null && remoteName !== void 0 ? remoteName : (remoteName = 'origin');
localBranchName !== null && localBranchName !== void 0 ? localBranchName : (localBranchName = 'main');
remoteBranchName !== null && remoteBranchName !== void 0 ? remoteBranchName : (remoteBranchName = 'main');
const urlOfRemote = await isomorphic_git_1.default.getConfig({
fs: fs_1.default,
dir: workingDir,
path: `remote.${remoteName}.url`,
});
if (urlOfRemote === undefined) {
throw new git_documentdb_remote_errors_1.InvalidGitRemoteError(`remote '${remoteName}' does not exist`);
}
(_a = remoteOptions.retry) !== null && _a !== void 0 ? _a : (remoteOptions.retry = const_1.NETWORK_RETRY);
(_b = remoteOptions.retryInterval) !== null && _b !== void 0 ? _b : (remoteOptions.retryInterval = const_1.NETWORK_RETRY_INTERVAL);
const pushOption = {
fs: fs_1.default,
dir: workingDir,
http: node_1.default,
url: remoteOptions.remoteUrl,
remote: remoteName,
ref: localBranchName,
remoteRef: remoteBranchName,
};
const cred = createCredentialCallback(remoteOptions);
if (cred) {
pushOption.onAuth = cred;
}
for (let i = 0; i < remoteOptions.retry + 1; i++) {
// eslint-disable-next-line no-await-in-loop
const res = await isomorphic_git_1.default.push(pushOption).catch(err => err);
let error = '';
if (res instanceof Error) {
error = res.toString();
}
else {
break;
}
// console.log('connect push error: ' + error);
switch (true) {
// NoRefspecError does not invoke because push does not need Remote when url is specified.
// case error.startsWith('NoRefspecError'):
// throw new InvalidGitRemoteError(error);
case error.startsWith('PushRejectedError:'):
throw new git_documentdb_remote_errors_1.UnfetchedCommitExistsError();
case error.startsWith('UrlParseError:'):
case error.startsWith('Error: getaddrinfo ENOTFOUND'):
throw new git_documentdb_remote_errors_1.InvalidURLFormatError(error);
case error.startsWith('Error: connect EACCES'):
case error.startsWith('Error: connect ECONNREFUSED'):
// isomorphic-git throws this when network is limited.
if (i >= remoteOptions.retry) {
throw new git_documentdb_remote_errors_1.NetworkError(error);
}
break;
case error.startsWith('HttpError: HTTP Error: 401'):
throw new git_documentdb_remote_errors_1.HTTPError401AuthorizationRequired(error);
case error.startsWith('HttpError: HTTP Error: 404 Not Found'):
throw new git_documentdb_remote_errors_1.HTTPError404NotFound(error);
case error.startsWith('HttpError: HTTP Error: 403 Forbidden'):
throw new git_documentdb_remote_errors_1.HTTPError403Forbidden(error);
default:
if (i >= remoteOptions.retry) {
throw new git_documentdb_remote_errors_1.CannotConnectError(error);
}
}
// eslint-disable-next-line no-await-in-loop
await (0, utils_1.sleep)(remoteOptions.retryInterval);
}
}
exports.push = push;
//# sourceMappingURL=remote-isomorphic-git.js.map