chore(git-sync): drop stray build/ artifacts re-introduced during rebase
build/ is gitignored and compiled in CI/Docker; a few files leaked back into the tree while replaying commits onto develop. Remove them so the package keeps a single source of truth (src/).
This commit is contained in:
70
packages/git-sync/build/engine/cycle.d.ts
vendored
70
packages/git-sync/build/engine/cycle.d.ts
vendored
@@ -1,70 +0,0 @@
|
||||
import { VaultGit } from "./git.js";
|
||||
import { GitSyncClient } from "./client.types.js";
|
||||
import { Settings } from "./settings.js";
|
||||
/**
|
||||
* Absolute-path filesystem primitives the cycle needs. Injected (not imported)
|
||||
* so the engine stays IO-free and unit-testable. `mkdir` is recursive; `rm` is
|
||||
* force (a missing file is a no-op).
|
||||
*/
|
||||
export interface CycleFs {
|
||||
readFile: (absPath: string) => Promise<string>;
|
||||
writeFile: (absPath: string, text: string) => Promise<void>;
|
||||
mkdir: (absDir: string) => Promise<void>;
|
||||
rm: (absPath: string) => Promise<void>;
|
||||
}
|
||||
export interface RunCycleDeps {
|
||||
spaceId: string;
|
||||
/** The Docmost seam (reads for pull, writes for push). */
|
||||
client: GitSyncClient;
|
||||
/** The per-space git vault (a real working repo). */
|
||||
vault: VaultGit;
|
||||
/** Engine settings; `vaultPath` roots the relPath -> absolute-path mapping. */
|
||||
settings: Settings;
|
||||
fs: CycleFs;
|
||||
log: (line: string) => void;
|
||||
/**
|
||||
* Delete-cap hook (the ONLY caller-specific policy). Called with the push
|
||||
* dry-run's planned delete count (`Number.POSITIVE_INFINITY` when the dry-run
|
||||
* itself failed, so the hook can fail safe) and the live client; returns the
|
||||
* client to use for the REAL apply. The default (omitted) applies every op
|
||||
* unmodified. gitmost uses it to neutralize deletes when over its cap.
|
||||
*
|
||||
* When omitted, NO dry-run is performed (one fewer push planning pass).
|
||||
*/
|
||||
resolveApplyClient?: (plannedDeletes: number, client: GitSyncClient) => GitSyncClient;
|
||||
}
|
||||
export interface RunCycleResult {
|
||||
ran: boolean;
|
||||
/** Set when the cycle short-circuited without running pull/push. */
|
||||
skipped?: "merge-in-progress";
|
||||
pull?: {
|
||||
written: number;
|
||||
deleted: number;
|
||||
conflict: boolean;
|
||||
};
|
||||
push?: {
|
||||
mode: string;
|
||||
failures: number;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Run ONE full reconcile cycle for a space: PULL (Docmost -> vault) then PUSH
|
||||
* (vault -> Docmost), under the engine's required branch choreography. This is
|
||||
* the single entry point the app drives — it owns the staging order so it can
|
||||
* never drift from the engine it ships with.
|
||||
*
|
||||
* Staging (the ⭐ data-loss-critical order, SPEC §6/§9):
|
||||
* 1. assertGitAvailable + ensureRepo (the git state store must exist).
|
||||
* 2. refuse on an unresolved merge (a prior conflicting pull); next checkout
|
||||
* would fail otherwise.
|
||||
* 3. ensureBranch('docmost','main') + checkout('docmost'). Pull writes MUST
|
||||
* land on `docmost`, not `main`: applyPullActions commits on `docmost`,
|
||||
* then checks out `main` and merges docmost -> main. Writing Docmost
|
||||
* content straight onto `main` would clobber local file edits before push
|
||||
* can diff them.
|
||||
* 4. PULL: readExisting -> listSpaceTree -> computePullActions -> apply.
|
||||
* 5. PUSH: optional dry-run to feed the delete-cap hook, then the real apply.
|
||||
*
|
||||
* Lock + cap POLICY live in the caller; this owns only the mechanics.
|
||||
*/
|
||||
export declare function runCycle(deps: RunCycleDeps): Promise<RunCycleResult>;
|
||||
@@ -1,97 +0,0 @@
|
||||
import { readExisting, computePullActions, applyPullActions } from "./pull.js";
|
||||
import { runPush } from "./push.js";
|
||||
/**
|
||||
* Run ONE full reconcile cycle for a space: PULL (Docmost -> vault) then PUSH
|
||||
* (vault -> Docmost), under the engine's required branch choreography. This is
|
||||
* the single entry point the app drives — it owns the staging order so it can
|
||||
* never drift from the engine it ships with.
|
||||
*
|
||||
* Staging (the ⭐ data-loss-critical order, SPEC §6/§9):
|
||||
* 1. assertGitAvailable + ensureRepo (the git state store must exist).
|
||||
* 2. refuse on an unresolved merge (a prior conflicting pull); next checkout
|
||||
* would fail otherwise.
|
||||
* 3. ensureBranch('docmost','main') + checkout('docmost'). Pull writes MUST
|
||||
* land on `docmost`, not `main`: applyPullActions commits on `docmost`,
|
||||
* then checks out `main` and merges docmost -> main. Writing Docmost
|
||||
* content straight onto `main` would clobber local file edits before push
|
||||
* can diff them.
|
||||
* 4. PULL: readExisting -> listSpaceTree -> computePullActions -> apply.
|
||||
* 5. PUSH: optional dry-run to feed the delete-cap hook, then the real apply.
|
||||
*
|
||||
* Lock + cap POLICY live in the caller; this owns only the mechanics.
|
||||
*/
|
||||
export async function runCycle(deps) {
|
||||
const { spaceId, client, vault, settings, fs, log, resolveApplyClient } = deps;
|
||||
const vaultRoot = settings.vaultPath;
|
||||
const abs = (relPath) => `${vaultRoot}/${relPath}`;
|
||||
// 1. The engine state store is git: make sure the repo + branches exist
|
||||
// before any tracked-file listing or diff.
|
||||
await vault.assertGitAvailable();
|
||||
await vault.ensureRepo();
|
||||
// 2. Refuse to run on top of an unresolved merge (SPEC §9): a prior
|
||||
// conflicting pull leaves the vault mid-merge; the next checkout would fail.
|
||||
if (await vault.isMergeInProgress()) {
|
||||
log(`vault has an unresolved merge — resolve it (or 'git merge --abort') ` +
|
||||
`and re-run (SPEC §9); skipping cycle.`);
|
||||
return { ran: false, skipped: "merge-in-progress" };
|
||||
}
|
||||
// 3. Pull writes happen on `docmost`; be on it BEFORE applying (see docstring).
|
||||
await vault.ensureBranch("docmost", "main");
|
||||
await vault.checkout("docmost");
|
||||
// 4. PULL --------------------------------------------------------------------
|
||||
const existing = await readExisting({
|
||||
listTracked: () => vault.listTrackedFiles("*.md"),
|
||||
readFile: (relPath) => fs.readFile(abs(relPath)),
|
||||
});
|
||||
const tree = await client.listSpaceTree(spaceId);
|
||||
const pullActions = computePullActions({
|
||||
pages: tree.pages,
|
||||
treeComplete: tree.complete,
|
||||
existing,
|
||||
});
|
||||
const pullResult = await applyPullActions({
|
||||
client,
|
||||
git: vault,
|
||||
writeFile: (absPath, text) => fs.writeFile(absPath, text),
|
||||
mkdir: (absDir) => fs.mkdir(absDir),
|
||||
rm: (absPath) => fs.rm(absPath),
|
||||
}, pullActions, vaultRoot);
|
||||
// 5. PUSH --------------------------------------------------------------------
|
||||
const pushDeps = {
|
||||
settings,
|
||||
git: vault,
|
||||
makeClient: () => client,
|
||||
readFile: (relPath) => fs.readFile(abs(relPath)),
|
||||
writeFile: (relPath, text) => fs.writeFile(abs(relPath), text),
|
||||
log,
|
||||
};
|
||||
let applyClient = client;
|
||||
if (resolveApplyClient) {
|
||||
// Plan the push as a DRY-RUN first to read the delete count, then let the
|
||||
// caller decide the apply client (e.g. neutralize deletes over a cap). A
|
||||
// failed dry-run yields Infinity so the hook can fail safe.
|
||||
let plannedDeletes;
|
||||
try {
|
||||
const dry = await runPush(pushDeps, { dryRun: true });
|
||||
plannedDeletes = dry.planned?.deletes ?? 0;
|
||||
}
|
||||
catch (err) {
|
||||
log(`push dry-run planning failed (${err instanceof Error ? err.message : String(err)}); deferring deletion policy to the cap hook (fail-safe).`);
|
||||
plannedDeletes = Number.POSITIVE_INFINITY;
|
||||
}
|
||||
applyClient = resolveApplyClient(plannedDeletes, client);
|
||||
}
|
||||
const pushResult = await runPush({ ...pushDeps, makeClient: () => applyClient }, { dryRun: false });
|
||||
return {
|
||||
ran: true,
|
||||
pull: {
|
||||
written: pullResult.written,
|
||||
deleted: pullResult.deleted,
|
||||
conflict: pullResult.merge.conflict,
|
||||
},
|
||||
push: {
|
||||
mode: pushResult.mode,
|
||||
failures: pushResult.failures?.length ?? 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
50
packages/git-sync/build/lib/page-file.d.ts
vendored
50
packages/git-sync/build/lib/page-file.d.ts
vendored
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* The native-Obsidian page-file format (design: docs/backlog/git-sync-thin-meta.md).
|
||||
* A page file is CLEAN markdown with a minimal YAML frontmatter carrying ONLY the
|
||||
* page's durable identity:
|
||||
*
|
||||
* ---
|
||||
* gitmost_id: 019ef6fc-2638-7ce1-9ce3-2756ce038480
|
||||
* ---
|
||||
* <clean markdown body>
|
||||
*
|
||||
* Everything else is derived (title = filename, parentPageId = enclosing folder,
|
||||
* spaceId = the vault, updatedAt = git). `gitmost_id` (a Docmost pageId) is the
|
||||
* only non-derivable bit and travels WITH the file so identity survives any move,
|
||||
* even one git's rename detection misses. Third-party editors (Obsidian, …) see
|
||||
* clean markdown; the frontmatter is hidden in their preview.
|
||||
*
|
||||
* No backward-compat with the old `docmost:meta` format: vaults are a cache, wiped
|
||||
* and rebuilt native. A file WITHOUT a `gitmost_id` frontmatter is an un-tracked
|
||||
* (e.g. hand-written) file -> the caller ADOPTS it (creates a page, writes the id).
|
||||
*/
|
||||
/**
|
||||
* The frontmatter key carrying the Docmost pageId. NAMESPACED (not a bare `id`)
|
||||
* so it never collides with a user's own frontmatter fields.
|
||||
*/
|
||||
export declare const ID_KEY = "gitmost_id";
|
||||
/**
|
||||
* Parse a page file into its identity (`id`) and clean markdown `body`. Tolerant:
|
||||
* a file with no frontmatter (a hand-written third-party file) returns `id: null`
|
||||
* and the whole text as the body — the caller then ADOPTS it (creates a page,
|
||||
* writes the id back).
|
||||
*
|
||||
* KNOWN LIMITATION (phase 4 — adoption, see docs/backlog/git-sync-thin-meta.md):
|
||||
* a leading frontmatter block is stripped from `body` even when it carries NO
|
||||
* `gitmost_id` but DOES carry the user's own Obsidian properties (`tags:` etc.).
|
||||
* On adoption those fields are not yet round-tripped — `serializePageFile`
|
||||
* write-back persists only `gitmost_id`. Preserving arbitrary user frontmatter
|
||||
* across the Docmost round-trip (BOTH adoption write-back AND the next pull's
|
||||
* re-serialize) is deferred to the adoption phase; until then, do NOT roll the
|
||||
* native format onto a real Obsidian vault whose notes carry properties.
|
||||
*/
|
||||
export declare function parsePageFile(full: string): {
|
||||
id: string | null;
|
||||
body: string;
|
||||
};
|
||||
/**
|
||||
* Serialize a page into the thin format: `id` frontmatter + a blank line + the
|
||||
* clean body + a trailing newline. Deterministic so an unchanged page re-syncs to
|
||||
* byte-identical output (no churn — the loop-guard relies on it).
|
||||
*/
|
||||
export declare function serializePageFile(id: string, body: string): string;
|
||||
@@ -1,72 +0,0 @@
|
||||
/**
|
||||
* The native-Obsidian page-file format (design: docs/backlog/git-sync-thin-meta.md).
|
||||
* A page file is CLEAN markdown with a minimal YAML frontmatter carrying ONLY the
|
||||
* page's durable identity:
|
||||
*
|
||||
* ---
|
||||
* gitmost_id: 019ef6fc-2638-7ce1-9ce3-2756ce038480
|
||||
* ---
|
||||
* <clean markdown body>
|
||||
*
|
||||
* Everything else is derived (title = filename, parentPageId = enclosing folder,
|
||||
* spaceId = the vault, updatedAt = git). `gitmost_id` (a Docmost pageId) is the
|
||||
* only non-derivable bit and travels WITH the file so identity survives any move,
|
||||
* even one git's rename detection misses. Third-party editors (Obsidian, …) see
|
||||
* clean markdown; the frontmatter is hidden in their preview.
|
||||
*
|
||||
* No backward-compat with the old `docmost:meta` format: vaults are a cache, wiped
|
||||
* and rebuilt native. A file WITHOUT a `gitmost_id` frontmatter is an un-tracked
|
||||
* (e.g. hand-written) file -> the caller ADOPTS it (creates a page, writes the id).
|
||||
*/
|
||||
/**
|
||||
* The frontmatter key carrying the Docmost pageId. NAMESPACED (not a bare `id`)
|
||||
* so it never collides with a user's own frontmatter fields.
|
||||
*/
|
||||
export const ID_KEY = "gitmost_id";
|
||||
/** Leading YAML frontmatter block: `---\n…\n---` at the very start of the file. */
|
||||
const FRONTMATTER_RE = /^?---\n([\s\S]*?)\n---\n?/;
|
||||
/** The top-level `<ID_KEY>: <value>` line inside the frontmatter (quotes optional). */
|
||||
function readIdFromYaml(yaml) {
|
||||
const re = new RegExp(`^${ID_KEY}:\\s*(.+?)\\s*$`);
|
||||
for (const line of yaml.split("\n")) {
|
||||
const m = line.match(re);
|
||||
if (m) {
|
||||
const v = m[1].trim().replace(/^["']|["']$/g, "");
|
||||
return v === "" ? null : v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Parse a page file into its identity (`id`) and clean markdown `body`. Tolerant:
|
||||
* a file with no frontmatter (a hand-written third-party file) returns `id: null`
|
||||
* and the whole text as the body — the caller then ADOPTS it (creates a page,
|
||||
* writes the id back).
|
||||
*
|
||||
* KNOWN LIMITATION (phase 4 — adoption, see docs/backlog/git-sync-thin-meta.md):
|
||||
* a leading frontmatter block is stripped from `body` even when it carries NO
|
||||
* `gitmost_id` but DOES carry the user's own Obsidian properties (`tags:` etc.).
|
||||
* On adoption those fields are not yet round-tripped — `serializePageFile`
|
||||
* write-back persists only `gitmost_id`. Preserving arbitrary user frontmatter
|
||||
* across the Docmost round-trip (BOTH adoption write-back AND the next pull's
|
||||
* re-serialize) is deferred to the adoption phase; until then, do NOT roll the
|
||||
* native format onto a real Obsidian vault whose notes carry properties.
|
||||
*/
|
||||
export function parsePageFile(full) {
|
||||
const text = (full ?? "").replace(/\r\n/g, "\n");
|
||||
// Native format: a `gitmost_id` YAML frontmatter. Anything else (no frontmatter,
|
||||
// or frontmatter without the key) is an un-tracked file -> adopt.
|
||||
const fm = text.match(FRONTMATTER_RE);
|
||||
if (fm) {
|
||||
return { id: readIdFromYaml(fm[1]), body: text.slice(fm[0].length).trim() };
|
||||
}
|
||||
return { id: null, body: text.trim() };
|
||||
}
|
||||
/**
|
||||
* Serialize a page into the thin format: `id` frontmatter + a blank line + the
|
||||
* clean body + a trailing newline. Deterministic so an unchanged page re-syncs to
|
||||
* byte-identical output (no churn — the loop-guard relies on it).
|
||||
*/
|
||||
export function serializePageFile(id, body) {
|
||||
return `---\n${ID_KEY}: ${id}\n---\n\n${body.trim()}\n`;
|
||||
}
|
||||
Reference in New Issue
Block a user