refactor(git-sync): internalize the engine — first-class ESM, no vendoring bridge (#119 review)

Closes the architecture item from the #119 review: drop the "vendored from
docmost-sync" framing and the CJS↔ESM `Function('import()')` bridge so the engine
is a normal first-class gitmost package.

Part 1 — vendoring markers removed (prose only, zero behavior change): reworded
"VENDORED into gitmost" / "vendored from docmost-sync" / "Engine LOGIC is
byte-identical" / "it's a port" comments across the engine. Behavior-bearing
strings are untouched: BOT_AUTHOR_NAME/EMAIL and the `Docmost-Sync-Source:`
provenance trailers (changing them would break git authorship + the loop-guard).

Part 2 — the package is now ESM (matching the sibling @docmost/mcp): `type: module`,
tsconfig Node16, `.js` extensions on relative imports, and a static
`import { marked }` replacing the `new Function('return import(...)')` /
`loadMarked` hack — the bridge is GONE from the package. The CommonJS NestJS
server loads the now-ESM engine via a new `git-sync.loader.ts` that mirrors the
existing `docmost-client.loader.ts` mcp loader exactly (Function-indirected
dynamic import + cached promise + retry-on-reject). The 4 server consumers
(orchestrator/datasource/vault-registry/git-http-backend) call `await loadGitSync()`
for value exports; types stay `import type` (erased). The converter-gate spec —
which needs the real converter — loads the package's TS source via a jest
moduleNameMapper + isolatedModules (documented in that spec); the other git-sync
specs mock the loader.

Verified: engine builds pure ESM (no Function/require leftover), vitest 614,
editor-ext build, server + client tsc, full server jest 1397/0. Live stand
smoke-test: server starts clean on the ESM engine (no ERR_REQUIRE_ESM), a real
sync cycle runs through the loader, and the basic e2e suite is 12/12 (clone via
git-http-backend, push, pull, delete, 3-way merge — all through the new loader).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
claude code agent 227
2026-06-24 14:23:40 +03:00
parent c366190db6
commit 5e63db575b
29 changed files with 270 additions and 205 deletions

View File

@@ -1,4 +1,4 @@
// Unit tests for the git-sync control plane. The vendored engine's `runCycle`
// Unit tests for the git-sync control plane. The engine's `runCycle`
// (which owns the PULL->PUSH branch choreography) is mocked so we exercise ONLY
// the orchestrator's wiring: gating, the Redis leader lock + in-process mutex
// (via SpaceLockService), the delete-cap POLICY it injects as `resolveApplyClient`,
@@ -7,13 +7,18 @@
// mechanics themselves are covered by the engine's own cycle round-trip spec.
//
// The engine mock must be declared before importing the orchestrator so the
// module-graph import binds to the mocked function.
jest.mock('@docmost/git-sync', () => ({
runCycle: jest.fn(),
// runtime `loadGitSync()` bridge resolves to the mocked `runCycle` (the ESM
// `@docmost/git-sync` package cannot be `require()`d under jest). The `mock`
// prefix lets the hoisted factory reference it.
const mockRunCycle = jest.fn();
jest.mock('../git-sync.loader', () => ({
loadGitSync: jest.fn(async () => ({
runCycle: mockRunCycle,
})),
}));
import { Logger } from '@nestjs/common';
import { runCycle } from '@docmost/git-sync';
import {
GitSyncOrchestrator,
GitSyncLockHeldError,
@@ -22,7 +27,7 @@ import { SpaceLockService } from './space-lock.service';
type AnyMock = jest.Mock;
const runCycleMock = runCycle as unknown as AnyMock;
const runCycleMock = mockRunCycle as unknown as AnyMock;
/** The default happy-path cycle result the engine returns. */
const OK_CYCLE = {