git-edge 0.1.2

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cache.ts","../src/errors.ts","../src/merge.ts","../src/repo.ts"],"sourcesContent":["/**\n * Generalized parsed-object LRU cache for isomorphic-git.\n *\n * Stores JS values directly — avoids JSON.parse on every hot-path read.\n * Configurable maxSize (bytes estimate via JSON.stringify.length) and TTL.\n *\n * Edge-compatible: no node: imports.\n */\nimport { LRUCache } from \"lru-cache\";\n\nexport interface ParsedObjectCacheOptions {\n\t/** Maximum estimated size in bytes (default: 256 MB). */\n\tmaxSize?: number;\n\t/** Time-to-live in milliseconds (default: 1 hour). */\n\tttl?: number;\n}\n\nexport interface ParsedObjectStore {\n\tget<T extends object>(key: string): T | null;\n\tset<T extends object>(key: string, value: T): void;\n\tdelete(key: string): void;\n\t/** Delete all keys matching a prefix. */\n\tinvalidatePrefix(prefix: string): void;\n}\n\n/**\n * Create a parsed-object cache instance.\n *\n * `key` convention: `\"${oid}:${format}\"` or any caller-chosen string.\n * `sizeCalculation` uses `JSON.stringify(v).length` as a byte estimate.\n */\nexport function createParsedObjectCache(\n\toptions?: ParsedObjectCacheOptions,\n): ParsedObjectStore {\n\tconst maxSize = options?.maxSize ?? 256 * 1024 * 1024;\n\tconst ttl = options?.ttl ?? 3600 * 1000;\n\n\tconst cache = new LRUCache<string, object>({\n\t\tmaxSize,\n\t\tsizeCalculation: (v) => JSON.stringify(v).length,\n\t\tttl,\n\t});\n\n\treturn {\n\t\tget<T extends object>(key: string): T | null {\n\t\t\treturn (cache.get(key) as T) ?? null;\n\t\t},\n\t\tset<T extends object>(key: string, value: T): void {\n\t\t\tcache.set(key, value);\n\t\t},\n\t\tdelete(key: string): void {\n\t\t\tcache.delete(key);\n\t\t},\n\t\tinvalidatePrefix(prefix: string): void {\n\t\t\tfor (const k of cache.keys()) {\n\t\t\t\tif (k.startsWith(prefix)) cache.delete(k);\n\t\t\t}\n\t\t},\n\t};\n}\n","/**\n * Error types for git-edge.\n *\n * Edge-compatible: no node: imports.\n */\n\n/** Base class for all git-edge errors. */\nexport class GitEdgeError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"GitEdgeError\";\n\t\tError.captureStackTrace?.(this, this.constructor);\n\t}\n}\n\n/** Thrown when a three-way merge has unresolvable conflicts. */\nexport class GitMergeConflictError extends GitEdgeError {\n\treadonly conflictingPaths: string[];\n\n\tconstructor(conflictingPaths: string[]) {\n\t\tsuper(\n\t\t\t`Merge conflict in ${conflictingPaths.length} file(s): ${conflictingPaths.join(\", \")}`,\n\t\t);\n\t\tthis.name = \"GitMergeConflictError\";\n\t\tthis.conflictingPaths = conflictingPaths;\n\t}\n}\n","/**\n * Object-level three-way merge for isomorphic-git.\n *\n * Merges two commits by walking their trees directly — no worktree, no disk.\n * Returns the merged commit OID, or throws MergeConflictError with the list of\n * conflicting file paths.\n *\n * Edge-compatible: no node: imports, uses only isomorphic-git object APIs.\n */\n\nimport type { FsClient } from \"isomorphic-git\";\nimport git from \"isomorphic-git\";\nimport { GitMergeConflictError } from \"./errors.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Options for threeWayMerge. */\nexport interface MergeOpts {\n\t/** Merge commit message (default: \"Merge <source> into <target>\"). */\n\tmessage?: string;\n\t/** Author name (default: \"Git Edge\"). */\n\tauthorName?: string;\n\t/** Author email (default: \"git-edge@local\"). */\n\tauthorEmail?: string;\n}\n\n/** Result of a successful merge. */\nexport interface MergeResult {\n\tcommitOid: string;\n}\n\n/** Analysis of whether a merge is possible. */\nexport interface MergeAnalysis {\n\t/** True if both refs resolved. */\n\tcanMerge: boolean;\n\t/** True if source is an ancestor of target (fast-forward possible). */\n\tfastForward: boolean;\n\t/** True if source and target diverged. */\n\tdiverged: boolean;\n}\n\n/** The repo fs+gitdir shape used by isomorphic-git. */\nexport interface Repo {\n\tfs: FsClient;\n\tgitdir: string;\n\tcache?: object;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction defaultAuthor() {\n\treturn {\n\t\tname: \"Git Edge\",\n\t\temail: \"git-edge@local\",\n\t\ttimestamp: Math.floor(Date.now() / 1000),\n\t\ttimezoneOffset: 0,\n\t};\n}\n\n/** Read a tree object into a flat map of path → oid. */\nasync function _flattenTree(\n\trepo: Repo,\n\ttreeOid: string,\n): Promise<Map<string, string>> {\n\tconst map = new Map<string, string>();\n\tconst entries = (await git.readTree({ ...repo, oid: treeOid })).tree;\n\tfor (const entry of entries) {\n\t\tmap.set(entry.path, entry.oid);\n\t}\n\treturn map;\n}\n\n/** Recursively flatten a tree into path → oid entries. */\nasync function flattenTreeDeep(\n\trepo: Repo,\n\ttreeOid: string,\n\tprefix: string,\n): Promise<Map<string, string>> {\n\tconst map = new Map<string, string>();\n\tconst entries = (await git.readTree({ ...repo, oid: treeOid })).tree;\n\tawait Promise.all(\n\t\tentries.map(async (entry) => {\n\t\t\tconst fullPath = prefix ? `${prefix}/${entry.path}` : entry.path;\n\t\t\tif (entry.type === \"tree\") {\n\t\t\t\tconst sub = await flattenTreeDeep(repo, entry.oid, fullPath);\n\t\t\t\tfor (const [k, v] of sub) map.set(k, v);\n\t\t\t} else {\n\t\t\t\tmap.set(fullPath, entry.oid);\n\t\t\t}\n\t\t}),\n\t);\n\treturn map;\n}\n\n/** Read a blob and return it as a Uint8Array. */\nasync function readBlob(repo: Repo, oid: string): Promise<Uint8Array> {\n\tconst { blob } = await git.readBlob({ ...repo, oid });\n\treturn blob;\n}\n\n/** Write a blob into the object store. */\nasync function writeBlob(repo: Repo, content: Uint8Array): Promise<string> {\n\treturn git.writeBlob({ ...repo, blob: content });\n}\n\n/** Write a tree object from a flat path→oid map. */\nasync function writeTreeObject(\n\trepo: Repo,\n\tentries: Array<{\n\t\tpath: string;\n\t\toid: string;\n\t\tmode: string;\n\t\ttype: \"blob\" | \"tree\";\n\t}>,\n): Promise<string> {\n\treturn git.writeObject({\n\t\t...repo,\n\t\ttype: \"tree\",\n\t\tobject: entries.map((e) => ({\n\t\t\tpath: e.path,\n\t\t\toid: e.oid,\n\t\t\tmode: e.mode,\n\t\t\ttype: e.type,\n\t\t})),\n\t\tformat: \"parsed\",\n\t});\n}\n\n/**\n * Write a tree from a flat path→{oid,mode} map by recursively building\n * intermediate trees for subdirectories.\n */\nasync function writeTreeFromFlat(\n\trepo: Repo,\n\tflat: Map<string, { oid: string; mode: string }>,\n): Promise<string> {\n\t// Group entries by first path segment\n\tconst root = new Map<string, { oid: string; mode: string }>();\n\tconst children = new Map<\n\t\tstring,\n\t\tMap<string, { oid: string; mode: string }>\n\t>();\n\n\tfor (const [p, v] of flat) {\n\t\tconst slash = p.indexOf(\"/\");\n\t\tif (slash === -1) {\n\t\t\troot.set(p, v);\n\t\t} else {\n\t\t\tconst dir = p.slice(0, slash);\n\t\t\tconst rest = p.slice(slash + 1);\n\t\t\tif (!children.has(dir)) children.set(dir, new Map());\n\t\t\tchildren.get(dir)?.set(rest, v);\n\t\t}\n\t}\n\n\tconst treeEntries: Array<{\n\t\tpath: string;\n\t\toid: string;\n\t\tmode: string;\n\t\ttype: \"blob\" | \"tree\";\n\t}> = [];\n\n\tfor (const [name, { oid, mode }] of root) {\n\t\ttreeEntries.push({ path: name, oid, mode, type: \"blob\" });\n\t}\n\n\tfor (const [dirName, subMap] of children) {\n\t\tconst subOid = await writeTreeFromFlat(repo, subMap);\n\t\ttreeEntries.push({\n\t\t\tpath: dirName,\n\t\t\toid: subOid,\n\t\t\tmode: \"040000\",\n\t\t\ttype: \"tree\",\n\t\t});\n\t}\n\n\treturn writeTreeObject(repo, treeEntries);\n}\n\n/**\n * Simple byte-level 3-way merge on two Uint8Array contents.\n *\n * This is a line-level merge using a basic diff algorithm.\n * For production quality, callers should use the `diff` library —\n * this is the fallback for edge environments where `diff` is not available.\n */\nfunction mergeContents(\n\tbase: Uint8Array,\n\tours: Uint8Array,\n\ttheirs: Uint8Array,\n): Uint8Array {\n\tconst baseLines = decodeLines(base);\n\tconst oursLines = decodeLines(ours);\n\tconst theirsLines = decodeLines(theirs);\n\n\t// If either side is identical to base, take the other\n\tif (arraysEqual(baseLines, oursLines)) return theirs;\n\tif (arraysEqual(baseLines, theirsLines)) return ours;\n\tif (arraysEqual(oursLines, theirsLines)) return ours;\n\n\t// Simple line-by-line merge: walk all three in lockstep\n\tconst result: string[] = [];\n\tlet bi = 0,\n\t\toi = 0,\n\t\tti = 0;\n\n\twhile (\n\t\tbi < baseLines.length ||\n\t\toi < oursLines.length ||\n\t\tti < theirsLines.length\n\t) {\n\t\tconst b = bi < baseLines.length ? baseLines[bi] : undefined;\n\t\tconst o = oi < oursLines.length ? oursLines[oi] : undefined;\n\t\tconst t = ti < theirsLines.length ? theirsLines[ti] : undefined;\n\n\t\tif (b === o && b === t) {\n\t\t\t// All three agree\n\t\t\tresult.push(b!);\n\t\t\tbi++;\n\t\t\toi++;\n\t\t\tti++;\n\t\t} else if (b === o) {\n\t\t\t// Only theirs changed\n\t\t\tresult.push(t!);\n\t\t\tbi++;\n\t\t\toi++;\n\t\t\tti++;\n\t\t} else if (b === t) {\n\t\t\t// Only ours changed\n\t\t\tresult.push(o!);\n\t\t\tbi++;\n\t\t\toi++;\n\t\t\tti++;\n\t\t} else {\n\t\t\t// Both changed — conflict: take ours + theirs separated by conflict markers\n\t\t\tresult.push(`<<<<<<< ours`);\n\t\t\tresult.push(o!);\n\t\t\tresult.push(`=======`);\n\t\t\tresult.push(t!);\n\t\t\tresult.push(`>>>>>>> theirs`);\n\t\t\tbi++;\n\t\t\toi++;\n\t\t\tti++;\n\t\t}\n\t}\n\n\treturn encodeLines(result);\n}\n\nfunction decodeLines(data: Uint8Array): string[] {\n\tconst text = new TextDecoder().decode(data);\n\tif (text === \"\") return [];\n\treturn text.split(\"\\n\");\n}\n\nfunction encodeLines(lines: string[]): Uint8Array {\n\treturn new TextEncoder().encode(lines.join(\"\\n\"));\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n\tif (a.length !== b.length) return false;\n\tfor (let i = 0; i < a.length; i++) {\n\t\tif (a[i] !== b[i]) return false;\n\t}\n\treturn true;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Analyze whether a merge is possible between two refs.\n *\n * Returns `canMerge`, `fastForward`, and `diverged` — does not attempt\n * a real content merge (no conflict detection in analysis).\n *\n * `canMerge: false` means one of the refs didn't resolve (deleted branch,\n * typo) — a `NotFoundError` from `git.resolveRef`. Any other failure (a\n * network/storage error reading the ref, a corrupted repo) is rethrown\n * rather than folded into `canMerge: false`, which would otherwise report\n * \"this branch doesn't exist\" for a transient failure that has nothing to\n * do with whether the branches can merge.\n */\nexport async function analyzeMerge(\n\trepo: Repo,\n\tsourceRef: string,\n\ttargetRef: string,\n): Promise<MergeAnalysis> {\n\ttry {\n\t\tconst sourceOid = await git.resolveRef({ ...repo, ref: sourceRef });\n\t\tconst targetOid = await git.resolveRef({ ...repo, ref: targetRef });\n\n\t\tconst isDescendant = await git.isDescendent({\n\t\t\t...repo,\n\t\t\toid: sourceOid,\n\t\t\tancestor: targetOid,\n\t\t});\n\n\t\treturn {\n\t\t\tcanMerge: true,\n\t\t\tfastForward: isDescendant,\n\t\t\tdiverged: !isDescendant,\n\t\t};\n\t} catch (err) {\n\t\tif ((err as { code?: string })?.code !== \"NotFoundError\") {\n\t\t\tthrow err;\n\t\t}\n\t\treturn { canMerge: false, fastForward: false, diverged: false };\n\t}\n}\n\n/**\n * Perform an object-level three-way merge.\n *\n * - If source is an ancestor of target (fast-forward), moves target ref to source.\n * - If target is an ancestor of source (reverse FF), moves target ref to source.\n * - Otherwise, performs a true 3-way merge at the tree level.\n *\n * Throws `GitMergeConflictError` if the merge has unresolvable conflicts.\n */\nexport async function threeWayMerge(\n\trepo: Repo,\n\tsourceRef: string,\n\ttargetRef: string,\n\topts?: MergeOpts,\n): Promise<MergeResult> {\n\tconst [sourceOid, targetOid] = await Promise.all([\n\t\tgit.resolveRef({ ...repo, ref: sourceRef }),\n\t\tgit.resolveRef({ ...repo, ref: targetRef }),\n\t]);\n\n\tif (sourceOid === targetOid) {\n\t\treturn { commitOid: sourceOid };\n\t}\n\n\t// Fast-forward: source is ahead of target\n\tconst isSourceAhead = await git.isDescendent({\n\t\t...repo,\n\t\toid: sourceOid,\n\t\tancestor: targetOid,\n\t});\n\tif (isSourceAhead) {\n\t\tawait git.writeRef({\n\t\t\t...repo,\n\t\t\tref: targetRef,\n\t\t\tvalue: sourceOid,\n\t\t\tforce: true,\n\t\t});\n\t\treturn { commitOid: sourceOid };\n\t}\n\n\t// Reverse fast-forward: target is ahead of source\n\tconst isTargetAhead = await git.isDescendent({\n\t\t...repo,\n\t\toid: targetOid,\n\t\tancestor: sourceOid,\n\t});\n\tif (isTargetAhead) {\n\t\tawait git.writeRef({\n\t\t\t...repo,\n\t\t\tref: targetRef,\n\t\t\tvalue: sourceOid,\n\t\t\tforce: true,\n\t\t});\n\t\treturn { commitOid: sourceOid };\n\t}\n\n\t// True 3-way merge\n\tconst baseOids = await git.findMergeBase({\n\t\t...repo,\n\t\toids: [sourceOid, targetOid],\n\t});\n\n\tif (!baseOids || baseOids.length === 0) {\n\t\tthrow new Error(\"No merge base found between source and target\");\n\t}\n\n\tconst baseOid = baseOids[0];\n\n\t// Read commit objects to get their trees\n\tconst [baseCommit, sourceCommit, targetCommit] = await Promise.all([\n\t\tgit.readCommit({ ...repo, oid: baseOid }),\n\t\tgit.readCommit({ ...repo, oid: sourceOid }),\n\t\tgit.readCommit({ ...repo, oid: targetOid }),\n\t]);\n\n\tconst baseTreeOid = baseCommit.commit.tree;\n\tconst sourceTreeOid = sourceCommit.commit.tree;\n\tconst targetTreeOid = targetCommit.commit.tree;\n\n\t// Flatten all three trees\n\tconst [baseMap, sourceMap, targetMap] = await Promise.all([\n\t\tflattenTreeDeep(repo, baseTreeOid, \"\"),\n\t\tflattenTreeDeep(repo, sourceTreeOid, \"\"),\n\t\tflattenTreeDeep(repo, targetTreeOid, \"\"),\n\t]);\n\n\t// Collect all unique paths\n\tconst allPaths = new Set([\n\t\t...baseMap.keys(),\n\t\t...sourceMap.keys(),\n\t\t...targetMap.keys(),\n\t]);\n\n\tconst resultEntries = new Map<string, { oid: string; mode: string }>();\n\tconst conflictingPaths: string[] = [];\n\n\tfor (const filepath of allPaths) {\n\t\tconst baseOid = baseMap.get(filepath);\n\t\tconst sourceOid = sourceMap.get(filepath);\n\t\tconst targetOid = targetMap.get(filepath);\n\n\t\tconst _baseExists = baseOid !== undefined;\n\t\tconst sourceExists = sourceOid !== undefined;\n\t\tconst targetExists = targetOid !== undefined;\n\n\t\tif (sourceExists && !targetExists) {\n\t\t\t// Added in source, not in target — take source\n\t\t\tresultEntries.set(filepath, { oid: sourceOid!, mode: \"100644\" });\n\t\t} else if (!sourceExists && targetExists) {\n\t\t\t// Deleted in source, exists in target — keep target\n\t\t\tresultEntries.set(filepath, { oid: targetOid!, mode: \"100644\" });\n\t\t} else if (sourceExists && targetExists) {\n\t\t\t// Exists in both — check if both changed from base\n\t\t\tconst sourceChanged = sourceOid !== baseOid;\n\t\t\tconst targetChanged = targetOid !== baseOid;\n\n\t\t\tif (!sourceChanged && !targetChanged) {\n\t\t\t\t// Neither changed — keep as-is\n\t\t\t\tresultEntries.set(filepath, { oid: targetOid!, mode: \"100644\" });\n\t\t\t} else if (!sourceChanged) {\n\t\t\t\t// Only target changed — take target\n\t\t\t\tresultEntries.set(filepath, { oid: targetOid!, mode: \"100644\" });\n\t\t\t} else if (!targetChanged) {\n\t\t\t\t// Only source changed — take source\n\t\t\t\tresultEntries.set(filepath, { oid: sourceOid!, mode: \"100644\" });\n\t\t\t} else {\n\t\t\t\t// Both changed — attempt content merge\n\t\t\t\tconst [baseContent, sourceContent, targetContent] = await Promise.all([\n\t\t\t\t\treadBlob(repo, baseOid!),\n\t\t\t\t\treadBlob(repo, sourceOid!),\n\t\t\t\t\treadBlob(repo, targetOid!),\n\t\t\t\t]);\n\n\t\t\t\tif (\n\t\t\t\t\tbaseContent.length === sourceContent.length &&\n\t\t\t\t\tbaseContent.every((b, i) => b === sourceContent[i])\n\t\t\t\t) {\n\t\t\t\t\t// Source is identical to base — take target\n\t\t\t\t\tresultEntries.set(filepath, { oid: targetOid!, mode: \"100644\" });\n\t\t\t\t} else if (\n\t\t\t\t\tbaseContent.length === targetContent.length &&\n\t\t\t\t\tbaseContent.every((b, i) => b === targetContent[i])\n\t\t\t\t) {\n\t\t\t\t\t// Target is identical to base — take source\n\t\t\t\t\tresultEntries.set(filepath, { oid: sourceOid!, mode: \"100644\" });\n\t\t\t\t} else if (\n\t\t\t\t\tsourceContent.length === targetContent.length &&\n\t\t\t\t\tsourceContent.every((b, i) => b === targetContent[i])\n\t\t\t\t) {\n\t\t\t\t\t// Source and target are identical — keep either\n\t\t\t\t\tresultEntries.set(filepath, { oid: sourceOid!, mode: \"100644\" });\n\t\t\t\t} else {\n\t\t\t\t\t// Both truly changed — attempt line-level merge\n\t\t\t\t\tconst merged = mergeContents(\n\t\t\t\t\t\tbaseContent,\n\t\t\t\t\t\tsourceContent,\n\t\t\t\t\t\ttargetContent,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Check for conflict markers\n\t\t\t\t\tconst mergedText = new TextDecoder().decode(merged);\n\t\t\t\t\tif (\n\t\t\t\t\t\tmergedText.includes(\"<<<<<<< ours\") ||\n\t\t\t\t\t\tmergedText.includes(\">>>>>>> theirs\")\n\t\t\t\t\t) {\n\t\t\t\t\t\tconflictingPaths.push(filepath);\n\t\t\t\t\t\t// Still take the merged result with conflict markers\n\t\t\t\t\t\t// so the caller can see what happened\n\t\t\t\t\t\tconst mergedOid = await writeBlob(repo, merged);\n\t\t\t\t\t\tresultEntries.set(filepath, { oid: mergedOid, mode: \"100644\" });\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst mergedOid = await writeBlob(repo, merged);\n\t\t\t\t\t\tresultEntries.set(filepath, { oid: mergedOid, mode: \"100644\" });\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// Deleted in both — no-op\n\t\t}\n\t}\n\n\tif (conflictingPaths.length > 0) {\n\t\tthrow new GitMergeConflictError(conflictingPaths);\n\t}\n\n\t// Build the result tree\n\tconst resultTreeOid = await writeTreeFromFlat(repo, resultEntries);\n\n\t// Create the merge commit\n\tconst author = opts?.authorName\n\t\t? {\n\t\t\t\tname: opts.authorName,\n\t\t\t\temail: opts.authorEmail || \"git-edge@local\",\n\t\t\t\ttimestamp: Math.floor(Date.now() / 1000),\n\t\t\t\ttimezoneOffset: 0,\n\t\t\t}\n\t\t: defaultAuthor();\n\n\tconst message = opts?.message || `Merge ${sourceRef} into ${targetRef}`;\n\n\tconst mergeCommitOid = await git.commit({\n\t\t...repo,\n\t\tmessage,\n\t\ttree: resultTreeOid,\n\t\tparent: [targetOid, sourceOid],\n\t\tauthor,\n\t\tcommitter: author,\n\t});\n\n\t// Update the target ref\n\tawait git.writeRef({\n\t\t...repo,\n\t\tref: targetRef,\n\t\tvalue: mergeCommitOid,\n\t\tforce: true,\n\t});\n\n\treturn { commitOid: mergeCommitOid };\n}\n","/**\n * Repository management utilities.\n *\n * Edge-compatible helpers for isomorphic-git repos: bare repo init,\n * packfile-only disk usage, and isomorphic-git cache management.\n *\n * No node: imports. The `fs` parameter is caller-provided.\n */\n\nimport type { FsClient } from \"isomorphic-git\";\nimport git from \"isomorphic-git\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** The repo fs+gitdir shape used by isomorphic-git. */\nexport interface Repo {\n\tfs: FsClient;\n\tgitdir: string;\n\tcache?: object;\n}\n\n// ---------------------------------------------------------------------------\n// isomorphic-git cache management\n// ---------------------------------------------------------------------------\n\n/**\n * Per-repo isomorphic-git packfile cache.\n *\n * isomorphic-git re-parses a packfile index from scratch on every\n * readTree/log/readObject call unless callers share a `cache` object\n * across calls. Without this, operations that touch many objects\n * (e.g. walking commit history) pay that parse cost hundreds of times.\n * Objects are content-addressed/immutable so a long-lived per-repo\n * cache is safe.\n */\nconst repoCaches = new Map<string, object>();\n\nexport function getRepoCache(ownerKey: string, repoName: string): object {\n\tconst key = `${ownerKey}/${repoName}`;\n\tlet cache = repoCaches.get(key);\n\tif (!cache) {\n\t\tcache = {};\n\t\trepoCaches.set(key, cache);\n\t}\n\treturn cache;\n}\n\nexport function invalidateRepoCache(ownerKey: string, repoName: string): void {\n\trepoCaches.delete(`${ownerKey}/${repoName}`);\n}\n\n// ---------------------------------------------------------------------------\n// Repo init\n// ---------------------------------------------------------------------------\n\n/**\n * Initialize a new bare git repository.\n *\n * Accepts any fs implementation — works with node:fs, S3-backed fs,\n * or any isomorphic-git-compatible filesystem.\n *\n * Returns the gitdir path.\n */\nexport async function initBareRepo(\n\trepo: Repo,\n\tdefaultBranch: string = \"main\",\n): Promise<void> {\n\tawait git.init({\n\t\t...repo,\n\t\tdir: repo.gitdir,\n\t\tdefaultBranch,\n\t\tbare: true,\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Disk usage\n// ---------------------------------------------------------------------------\n\n/**\n * Estimate the disk usage of a bare repo's git objects.\n *\n * This walks the loose object directories and reads `.pack` file sizes\n * from the `objects/` directory. For S3-backed repos, this may be\n * expensive — prefer tracking size at write time if possible.\n *\n * The caller must provide a `stat` function that works with their fs.\n * For node:fs this is `fs.stat`; for edge runtimes it may be a HEAD\n * request to the S3 object.\n */\nexport async function estimateRepoSize(\n\trepo: Repo,\n\tstat: (path: string) => Promise<{ size: number }>,\n\tlist: (path: string) => Promise<string[]>,\n): Promise<number> {\n\tlet total = 0;\n\n\tasync function walk(dir: string): Promise<void> {\n\t\tlet entries: string[];\n\t\ttry {\n\t\t\tentries = await list(dir);\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\n\t\tconst sizes = await Promise.all(\n\t\t\tentries.map(async (name) => {\n\t\t\t\tconst fullPath = `${dir}/${name}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst s = await stat(fullPath);\n\t\t\t\t\treturn s.size;\n\t\t\t\t} catch {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\tfor (const size of sizes) total += size;\n\t}\n\n\tawait walk(`${repo.gitdir}/objects`);\n\treturn total;\n}\n"],"mappings":"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","names":["baseOid","sourceOid","targetOid","git"]}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "git-edge",
3
+ "version": "0.1.2",
4
+ "description": "High-level edge-compatible git operations — parsed-object cache, object-level three-way merge, and repo management utilities. Composes with git-fs-s3.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js",
10
+ "require": "./dist/index.cjs"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "sideEffects": false,
17
+ "scripts": {
18
+ "build": "tsup",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "coverage": "vitest run --coverage",
22
+ "typecheck": "tsc --noEmit",
23
+ "lint": "biome check src/ test/",
24
+ "lint:fix": "biome check --write src/ test/",
25
+ "format": "biome format --write src/ test/",
26
+ "prepublishOnly": "npm run typecheck && npm test && npm run build"
27
+ },
28
+ "keywords": [
29
+ "git",
30
+ "isomorphic-git",
31
+ "edge",
32
+ "serverless",
33
+ "merge",
34
+ "cache",
35
+ "three-way-merge"
36
+ ],
37
+ "author": "Nandan Varma",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/nandan-varma/git-edge.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/nandan-varma/git-edge/issues"
45
+ },
46
+ "homepage": "https://github.com/nandan-varma/git-edge#readme",
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "engines": {
51
+ "node": ">=18"
52
+ },
53
+ "dependencies": {
54
+ "lru-cache": "^11.2.1"
55
+ },
56
+ "peerDependencies": {
57
+ "git-fs-s3": ">=0.3.0",
58
+ "isomorphic-git": ">=1.24.0"
59
+ },
60
+ "devDependencies": {
61
+ "@biomejs/biome": "^2.3.8",
62
+ "git-fs-s3": "^0.3.0",
63
+ "@types/node": "^22.15.21",
64
+ "isomorphic-git": "^1.27.0",
65
+ "tsup": "^8.5.0",
66
+ "typescript": "^5.8.3",
67
+ "vitest": "^3.2.3"
68
+ }
69
+ }