UNPKG

git-documentdb

Version:

Offline-first database that syncs with Git

325 lines 13.2 kB
"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