git-documentdb
Version:
Offline-first database that syncs with Git
325 lines • 13.2 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 gitDDB source tree.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.calcDistance = exports.getCommitLogs = exports.getAndWriteLocalChanges = exports.getChanges = exports.getFatDocFromReadBlobResult = exports.getFatDocFromOid = exports.getFatDocFromData = exports.writeBlobToFile = void 0;
const path_1 = __importDefault(require("path"));
const isomorphic_git_1 = __importDefault(require("isomorphic-git"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const utils_1 = require("../utils");
const error_1 = require("../error");
const blob_1 = require("../crud/blob");
/**
* Write blob to file system
*
* @throws {@link Err.CannotCreateDirectoryError}
*/
async function writeBlobToFile(workingDir, name, data) {
const filePath = path_1.default.resolve(workingDir, name);
const dir = path_1.default.dirname(filePath);
await fs_extra_1.default.ensureDir(dir).catch((err) => {
return Promise.reject(new error_1.Err.CannotCreateDirectoryError(err.message));
});
await fs_extra_1.default.writeFile(filePath, data);
}
exports.writeBlobToFile = writeBlobToFile;
/**
* getFatDocFromData
*
* @throws {@link Err.InvalidJsonObjectError}
*/
async function getFatDocFromData(data, fullDocPath, docType, serializeFormat) {
let fatDoc;
const { oid } = await isomorphic_git_1.default.hashBlob({ object: data });
if (docType === 'json') {
const _id = serializeFormat.removeExtension(fullDocPath);
if (typeof data !== 'string') {
data = (0, utils_1.utf8decode)(data);
}
try {
const [, extension] = fullDocPath.match(/.+(\..+?)$/);
const jsonDoc = (0, blob_1.textToJsonDoc)(data, serializeFormat, extension);
if (jsonDoc._id !== undefined) {
// Overwrite _id property by _id if JsonDoc is created by GitDocumentedDB (_id !== undefined).
jsonDoc._id = _id;
}
fatDoc = {
_id,
name: fullDocPath,
fileOid: oid,
type: 'json',
doc: jsonDoc,
};
}
catch {
throw new error_1.Err.InvalidJsonObjectError(_id);
}
}
else if (docType === 'text') {
if (typeof data !== 'string') {
data = (0, utils_1.utf8decode)(data);
}
fatDoc = {
name: fullDocPath,
fileOid: oid,
type: 'text',
doc: data,
};
}
else if (docType === 'binary') {
fatDoc = {
name: fullDocPath,
fileOid: oid,
type: 'binary',
doc: data,
};
}
return fatDoc;
}
exports.getFatDocFromData = getFatDocFromData;
/**
* getFatDocFromOid
*
* @throws {@link Err.InvalidJsonObjectError} (from getFatDocFromReadBlobResult)
*/
async function getFatDocFromOid(workingDir, fullDocPath, fileOid, docType, serializeFormat) {
const readBlobResult = await isomorphic_git_1.default.readBlob({
fs: fs_extra_1.default,
dir: workingDir,
oid: fileOid,
});
return getFatDocFromReadBlobResult(fullDocPath, readBlobResult, docType, serializeFormat);
}
exports.getFatDocFromOid = getFatDocFromOid;
/**
* getFatDocFromReadBlobResult
*
* @throws {@link Err.InvalidJsonObjectError}
*/
function getFatDocFromReadBlobResult(fullDocPath, readBlobResult, docType, serializeFormat) {
let fatDoc;
if (docType === 'json') {
const _id = serializeFormat.removeExtension(fullDocPath);
const [, extension] = fullDocPath.match(/.+(\..+?)$/);
fatDoc = (0, blob_1.blobToJsonDoc)(_id, readBlobResult, true, serializeFormat, extension);
}
else if (docType === 'text') {
fatDoc = (0, blob_1.blobToText)(fullDocPath, readBlobResult, true);
}
else if (docType === 'binary') {
fatDoc = (0, blob_1.blobToBinary)(fullDocPath, readBlobResult, true);
}
return fatDoc;
}
exports.getFatDocFromReadBlobResult = getFatDocFromReadBlobResult;
/**
* Get changed files
*
* @throws {@link Err.InvalidJsonObjectError} (from getFatDocFromOid)
*
* @internal
*/
async function getChanges(workingDir, oldCommitOid, newCommitOid, serializeFormat) {
return await isomorphic_git_1.default.walk({
fs: fs_extra_1.default,
dir: workingDir,
trees: [isomorphic_git_1.default.TREE({ ref: oldCommitOid }), isomorphic_git_1.default.TREE({ ref: newCommitOid })],
// @ts-ignore
// eslint-disable-next-line complexity
map: async function (fullDocPath, [a, b]) {
// ignore directories
if (fullDocPath === '.') {
return;
}
if (oldCommitOid === undefined) {
// Must set null explicitly.
a = null;
}
const docType = serializeFormat.hasObjectExtension(fullDocPath)
? 'json'
: 'text';
if (docType === 'text') {
// TODO: select binary or text by .gitattribtues
}
const aType = a === null ? undefined : await a.type();
const bType = b === null ? undefined : await b.type();
if (aType === 'tree' || bType === 'tree') {
return;
}
// generate ids
const aOid = a === null ? undefined : await a.oid();
const bOid = b === null ? undefined : await b.oid();
let change;
if (bOid === undefined && aOid !== undefined) {
change = {
operation: 'delete',
old: await getFatDocFromOid(workingDir, fullDocPath, aOid, docType, serializeFormat),
};
}
else if (aOid === undefined && bOid !== undefined) {
change = {
operation: 'insert',
new: await getFatDocFromOid(workingDir, fullDocPath, bOid, docType, serializeFormat),
};
}
else if (aOid !== undefined && bOid !== undefined && aOid !== bOid) {
change = {
operation: 'update',
old: await getFatDocFromOid(workingDir, fullDocPath, aOid, docType, serializeFormat),
new: await getFatDocFromOid(workingDir, fullDocPath, bOid, docType, serializeFormat),
};
}
else {
return;
}
return change;
},
});
}
exports.getChanges = getChanges;
/**
* Get and write changed files on local
*
* @throws {@link Err.InvalidJsonObjectError} (from getFatDocFromOid)
* @throws {@link Err.CannotCreateDirectoryError} (from writeBlobToFile)
*
* @internal
*/
async function getAndWriteLocalChanges(workingDir, oldCommitOid, newCommitOid, serializeFormat) {
return await isomorphic_git_1.default.walk({
fs: fs_extra_1.default,
dir: workingDir,
trees: [isomorphic_git_1.default.TREE({ ref: oldCommitOid }), isomorphic_git_1.default.TREE({ ref: newCommitOid })],
// @ts-ignore
// eslint-disable-next-line complexity
map: async function (fullDocPath, [a, b]) {
// ignore directories
if (fullDocPath === '.') {
return;
}
const docType = serializeFormat.hasObjectExtension(fullDocPath)
? 'json'
: 'text';
if (docType === 'text') {
// TODO: select binary or text by .gitattribtues
}
const aType = a === null ? undefined : await a.type();
const bType = b === null ? undefined : await b.type();
if (aType === 'tree' || bType === 'tree') {
return;
}
// generate ids
const aOid = a === null ? undefined : await a.oid();
const bOid = b === null ? undefined : await b.oid();
let change;
if (bOid === undefined && aOid !== undefined) {
change = {
operation: 'delete',
old: await getFatDocFromOid(workingDir, fullDocPath, aOid, docType, serializeFormat),
};
await isomorphic_git_1.default.remove({ fs: fs_extra_1.default, dir: workingDir, filepath: fullDocPath });
const path = path_1.default.resolve(workingDir, fullDocPath);
await fs_extra_1.default.remove(path).catch(() => {
throw new error_1.Err.CannotDeleteDataError();
});
}
else if (aOid === undefined && bOid !== undefined) {
change = {
operation: 'insert',
new: await getFatDocFromOid(workingDir, fullDocPath, bOid, docType, serializeFormat),
};
if (change.new.type === 'json') {
await writeBlobToFile(workingDir, fullDocPath, serializeFormat.serialize(change.new.doc).data);
}
else if (change.new.type === 'text' || change.new.type === 'binary') {
await writeBlobToFile(workingDir, fullDocPath, change.new.doc);
}
await isomorphic_git_1.default.add({ fs: fs_extra_1.default, dir: workingDir, filepath: fullDocPath });
}
else if (aOid !== undefined && bOid !== undefined && aOid !== bOid) {
change = {
operation: 'update',
old: await getFatDocFromOid(workingDir, fullDocPath, aOid, docType, serializeFormat),
new: await getFatDocFromOid(workingDir, fullDocPath, bOid, docType, serializeFormat),
};
if (change.new.type === 'json') {
await writeBlobToFile(workingDir, fullDocPath, serializeFormat.serialize(change.new.doc).data);
}
else if (change.new.type === 'text' || change.new.type === 'binary') {
await writeBlobToFile(workingDir, fullDocPath, change.new.doc);
}
await isomorphic_git_1.default.add({ fs: fs_extra_1.default, dir: workingDir, filepath: fullDocPath });
}
else {
return;
}
return change;
},
});
}
exports.getAndWriteLocalChanges = getAndWriteLocalChanges;
/**
* Get commit logs by walking backward
*
* @remarks
*
* - Logs are sorted by topology. Ancestors are placed before descendants. Topic branches are placed before the main branch.
*
* - Walking stops when it reaches to walkToCommitOid or walkToCommitOid2.
*
* - Use walkToCommitOid2 when walkFromCommit has two parents.
*
* - walkToCommit is not included to return value.
*
* @internal
*/
async function getCommitLogs(workingDir, walkFromCommitOid, walkToCommitOid, walkToCommitOid2) {
// Return partial logs.
// See https://github.com/isomorphic-git/isomorphic-git/blob/main/src/commands/log.js
const parents = [await isomorphic_git_1.default.readCommit({ fs: fs_extra_1.default, dir: workingDir, oid: walkFromCommitOid })];
const history = [];
const commits = [];
while (parents.length > 0) {
const commit = parents.pop();
if (commit.oid === walkToCommitOid || commit.oid === walkToCommitOid2)
continue;
commits.push((0, utils_1.normalizeCommit)(commit));
// Add the parents of this commit to the queue
for (const oid of commit.commit.parent) {
// eslint-disable-next-line no-await-in-loop
const parentCommit = await isomorphic_git_1.default.readCommit({ fs: fs_extra_1.default, dir: workingDir, oid });
if (!history.map(myCommit => myCommit.oid).includes(parentCommit.oid)) {
history.push(parentCommit);
parents.push(parentCommit);
}
}
}
// The list is sorted by topology.
return commits.reverse();
}
exports.getCommitLogs = getCommitLogs;
/**
* Calc distance
*/
function calcDistance(baseCommitOid, localCommitOid, remoteCommitOid) {
if (baseCommitOid === undefined) {
return {
ahead: undefined,
behind: undefined,
};
}
return {
ahead: localCommitOid !== baseCommitOid ? 1 : 0,
behind: remoteCommitOid !== baseCommitOid ? 1 : 0,
};
}
exports.calcDistance = calcDistance;
//# sourceMappingURL=worker_utils.js.map