mrplex 0.0.1 → 0.0.3
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.
- package/README.md +5 -3
- package/dist/cli/main.js +10 -5
- package/dist/cli/main.js.map +1 -1
- package/dist/sync/converge.d.ts +27 -0
- package/dist/sync/converge.js +46 -0
- package/dist/sync/converge.js.map +1 -0
- package/dist/sync/daemon.d.ts +4 -0
- package/dist/sync/daemon.js +60 -7
- package/dist/sync/daemon.js.map +1 -1
- package/dist/sync/feed.d.ts +16 -0
- package/dist/sync/feed.js +81 -56
- package/dist/sync/feed.js.map +1 -1
- package/dist/sync/fs-store.js +10 -0
- package/dist/sync/fs-store.js.map +1 -1
- package/dist/sync/hot-path.d.ts +83 -0
- package/dist/sync/hot-path.js +232 -0
- package/dist/sync/hot-path.js.map +1 -0
- package/dist/sync/push.js +7 -31
- package/dist/sync/push.js.map +1 -1
- package/dist/sync/reconcile.d.ts +7 -1
- package/dist/sync/reconcile.js +59 -55
- package/dist/sync/reconcile.js.map +1 -1
- package/package.json +1 -1
package/dist/sync/feed.js
CHANGED
|
@@ -8,10 +8,14 @@
|
|
|
8
8
|
* absent file is a no-op; the dirty check prevents a replay from clobbering an
|
|
9
9
|
* edit made in a crash window. So a crash between apply and cursor-advance
|
|
10
10
|
* simply replays the batch harmlessly.
|
|
11
|
+
*
|
|
12
|
+
* better-sync.plan: hot files defer inbound effects into an in-memory map
|
|
13
|
+
* (cursor still advances); dirty+stale local files rebase onto remote current
|
|
14
|
+
* before parking a sibling.
|
|
11
15
|
*/
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { isDirty, isIgnored, readFileIntrinsics
|
|
16
|
+
import { materializeAt } from "./converge.js";
|
|
17
|
+
import { decideInbound, effectInbound, enqueueDeferred, pathIsHot, versionAtOrAhead, } from "./hot-path.js";
|
|
18
|
+
import { isDirty, isIgnored, readFileIntrinsics } from "./intrinsics.js";
|
|
15
19
|
/**
|
|
16
20
|
* Drain the safe feed from `since` to the live tip, applying each in-scope ref.
|
|
17
21
|
* Returns the final cursor to persist. Stops when a poll returns no new refs
|
|
@@ -21,11 +25,15 @@ export async function applyFeed(client, store, scope, opts) {
|
|
|
21
25
|
const log = opts.log ?? (() => { });
|
|
22
26
|
let cursor = opts.since;
|
|
23
27
|
let applied = 0;
|
|
28
|
+
let deferredCount = 0;
|
|
24
29
|
for (;;) {
|
|
25
30
|
const page = await client.history.since({ after_version: cursor, repo: opts.repo });
|
|
26
31
|
for (const ref of page.refs) {
|
|
27
|
-
|
|
32
|
+
const outcome = await applyRef(client, store, scope, opts, ref, log);
|
|
33
|
+
if (outcome === "applied")
|
|
28
34
|
applied++;
|
|
35
|
+
else if (outcome === "deferred")
|
|
36
|
+
deferredCount++;
|
|
29
37
|
}
|
|
30
38
|
// No forward progress → caught up (or a hot gap). Stop draining.
|
|
31
39
|
if (page.next_since === cursor || page.refs.length === 0) {
|
|
@@ -34,92 +42,109 @@ export async function applyFeed(client, store, scope, opts) {
|
|
|
34
42
|
}
|
|
35
43
|
cursor = page.next_since;
|
|
36
44
|
}
|
|
37
|
-
return { cursor, applied };
|
|
45
|
+
return { cursor, applied, deferred: deferredCount };
|
|
38
46
|
}
|
|
39
|
-
/** Apply one feed ref to the local store.
|
|
40
|
-
async function applyRef(client, store, scope,
|
|
47
|
+
/** Apply one feed ref to the local store. */
|
|
48
|
+
async function applyRef(client, store, scope, opts, ref, log) {
|
|
49
|
+
const settleMs = opts.settleMs ?? 0;
|
|
50
|
+
const nowMs = opts.nowMs?.() ?? Date.now();
|
|
51
|
+
const canDefer = opts.deferred !== undefined;
|
|
41
52
|
if (ref.op === "delete") {
|
|
42
|
-
// Remove the local file at prev_path IF clean; dirty ⇒ keep (resurrection
|
|
43
|
-
// happens on its next local-change cycle). §4.3 delete.
|
|
44
53
|
const target = ref.prev_path;
|
|
45
54
|
if (target === null || !scope.matches(target))
|
|
46
|
-
return
|
|
47
|
-
map?.delete(target);
|
|
55
|
+
return "noop";
|
|
56
|
+
opts.map?.delete(target);
|
|
48
57
|
const text = await store.read(target);
|
|
49
58
|
if (text === null)
|
|
50
|
-
return
|
|
59
|
+
return "noop";
|
|
51
60
|
const intr = readFileIntrinsics(text);
|
|
52
61
|
if (isIgnored(intr) || isDirty(intr))
|
|
53
|
-
return
|
|
62
|
+
return "noop"; // preserve local work
|
|
63
|
+
if (canDefer && (await pathIsHot(store, target, settleMs, nowMs))) {
|
|
64
|
+
enqueueDeferred(opts.deferred, target, ref, nowMs);
|
|
65
|
+
return "deferred";
|
|
66
|
+
}
|
|
54
67
|
await store.remove(target);
|
|
55
68
|
log(`feed delete\t${target}`);
|
|
56
|
-
return
|
|
69
|
+
return "applied";
|
|
57
70
|
}
|
|
58
71
|
if (ref.op === "move") {
|
|
59
72
|
const from = ref.prev_path;
|
|
60
73
|
if (from !== null && scope.matches(from)) {
|
|
61
|
-
map?.delete(from);
|
|
74
|
+
opts.map?.delete(from);
|
|
62
75
|
const text = await store.read(from);
|
|
63
76
|
if (text !== null) {
|
|
64
77
|
const intr = readFileIntrinsics(text);
|
|
65
|
-
if (!isIgnored(intr) && !isDirty(intr))
|
|
78
|
+
if (!isIgnored(intr) && !isDirty(intr)) {
|
|
79
|
+
if (canDefer && (await pathIsHot(store, from, settleMs, nowMs))) {
|
|
80
|
+
// Hold the whole move (including dest) until source is cold.
|
|
81
|
+
enqueueDeferred(opts.deferred, ref.path, ref, nowMs);
|
|
82
|
+
return "deferred";
|
|
83
|
+
}
|
|
66
84
|
await store.remove(from);
|
|
85
|
+
}
|
|
67
86
|
}
|
|
68
87
|
}
|
|
69
88
|
// Fall through to materialize at the destination path.
|
|
70
89
|
}
|
|
71
|
-
// create / update / move-destination: materialize at ref.path unless the
|
|
72
|
-
// local file already holds these bytes, is at-or-ahead of this ref, is
|
|
73
|
-
// dirty against a *newer* remote, or is $sync: ignore.
|
|
74
90
|
if (!scope.matches(ref.path))
|
|
75
|
-
return
|
|
91
|
+
return "noop";
|
|
76
92
|
const existing = await store.read(ref.path);
|
|
77
93
|
if (existing !== null) {
|
|
78
94
|
const intr = readFileIntrinsics(existing);
|
|
79
95
|
if (isIgnored(intr))
|
|
80
|
-
return
|
|
96
|
+
return "noop";
|
|
97
|
+
// Fast path: bytes already match — provenance repair or defer when hot.
|
|
81
98
|
if (intr.computed_hash === ref.content_hash) {
|
|
82
|
-
|
|
83
|
-
map?.set(ref.path, { version_id: ref.version_id, content_hash: ref.content_hash });
|
|
84
|
-
// Repair provenance only if the embedded version lags.
|
|
99
|
+
opts.map?.set(ref.path, { version_id: ref.version_id, content_hash: ref.content_hash });
|
|
85
100
|
if (intr.version === ref.version_id)
|
|
86
|
-
return
|
|
101
|
+
return "noop";
|
|
87
102
|
if (versionAtOrAhead(intr.version, ref.version_id))
|
|
88
|
-
return
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
return "noop";
|
|
104
|
+
const hot = await pathIsHot(store, ref.path, settleMs, nowMs);
|
|
105
|
+
if (hot && canDefer) {
|
|
106
|
+
enqueueDeferred(opts.deferred, ref.path, ref, nowMs);
|
|
107
|
+
return "deferred";
|
|
108
|
+
}
|
|
109
|
+
const v = await client.docs.get_version(opts.repo, ref.version_id);
|
|
110
|
+
await materializeAt(store, ref.path, v, { preserveMtime: true });
|
|
111
|
+
log(`feed adopt\t${ref.path}`);
|
|
112
|
+
return "applied";
|
|
92
113
|
}
|
|
93
|
-
// Local is at or ahead of this ref (echo of our push, or a replay of an
|
|
94
|
-
// older version). Never clobber and never park a sibling of something we
|
|
95
|
-
// already have — including when the user has typed more since (dirty).
|
|
96
114
|
if (versionAtOrAhead(intr.version, ref.version_id))
|
|
97
|
-
return
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
115
|
+
return "noop";
|
|
116
|
+
// Divergent local vs newer remote → decideInbound (rebase / defer / park).
|
|
117
|
+
const hot = await pathIsHot(store, ref.path, settleMs, nowMs);
|
|
118
|
+
const v = await client.docs.get_version(opts.repo, ref.version_id);
|
|
119
|
+
const decision = decideInbound({
|
|
120
|
+
localText: existing,
|
|
121
|
+
remote: v,
|
|
122
|
+
hot,
|
|
123
|
+
canDefer,
|
|
124
|
+
});
|
|
125
|
+
const result = await effectInbound(client, store, opts.repo, ref.path, decision, {
|
|
126
|
+
deferred: opts.deferred,
|
|
127
|
+
ref,
|
|
128
|
+
map: opts.map,
|
|
129
|
+
nowMs,
|
|
130
|
+
});
|
|
131
|
+
if (result === "deferred")
|
|
132
|
+
return "deferred";
|
|
133
|
+
if (result === "noop")
|
|
134
|
+
return "noop";
|
|
135
|
+
if (result === "parked")
|
|
103
136
|
log(`feed conflict\t${ref.path}`);
|
|
104
|
-
|
|
105
|
-
|
|
137
|
+
else if (decision.action === "rebase")
|
|
138
|
+
log(`feed rebase\t${ref.path}`);
|
|
139
|
+
else
|
|
140
|
+
log(`feed ${ref.op}\t${ref.path}`);
|
|
141
|
+
return "applied";
|
|
106
142
|
}
|
|
107
|
-
|
|
108
|
-
await
|
|
109
|
-
|
|
143
|
+
// Absent locally → materialize (unless somehow hot — can't be; no mtime).
|
|
144
|
+
const v = await client.docs.get_version(opts.repo, ref.version_id);
|
|
145
|
+
await materializeAt(store, ref.path, v);
|
|
146
|
+
opts.map?.set(ref.path, { version_id: ref.version_id, content_hash: ref.content_hash });
|
|
110
147
|
log(`feed ${ref.op}\t${ref.path}`);
|
|
111
|
-
return
|
|
112
|
-
}
|
|
113
|
-
/** True when the local embedded version is this ref or a later one. */
|
|
114
|
-
function versionAtOrAhead(localVersion, refVersion) {
|
|
115
|
-
if (!localVersion)
|
|
116
|
-
return false;
|
|
117
|
-
if (localVersion === refVersion)
|
|
118
|
-
return true;
|
|
119
|
-
const local = decodeVersionId(localVersion);
|
|
120
|
-
const ref = decodeVersionId(refVersion);
|
|
121
|
-
if (local === null || ref === null)
|
|
122
|
-
return false;
|
|
123
|
-
return local > ref;
|
|
148
|
+
return "applied";
|
|
124
149
|
}
|
|
125
150
|
//# sourceMappingURL=feed.js.map
|
package/dist/sync/feed.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feed.js","sourceRoot":"","sources":["../../src/sync/feed.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"feed.js","sourceRoot":"","sources":["../../src/sync/feed.ts"],"names":[],"mappings":"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"}
|
package/dist/sync/fs-store.js
CHANGED
|
@@ -44,6 +44,16 @@ export function createFsStore(root) {
|
|
|
44
44
|
async remove(docPath) {
|
|
45
45
|
await rm(toLocalPath(root, docPath), { force: true });
|
|
46
46
|
},
|
|
47
|
+
async mtime(docPath) {
|
|
48
|
+
try {
|
|
49
|
+
return (await stat(toLocalPath(root, docPath))).mtimeMs;
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (err.code === "ENOENT")
|
|
53
|
+
return null;
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
},
|
|
47
57
|
};
|
|
48
58
|
}
|
|
49
59
|
async function walk(root, dir, out) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs-store.js","sourceRoot":"","sources":["../../src/sync/fs-store.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fs-store.js","sourceRoot":"","sources":["../../src/sync/fs-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hot-path detection and inbound convergence decisions (better-sync.plan).
|
|
3
|
+
* Feed/reconcile call `decideInbound` then `effectInbound`; the daemon holds
|
|
4
|
+
* deferred refs in memory and retries them on later polls.
|
|
5
|
+
*/
|
|
6
|
+
import type { KernelClient } from "../client/kernel-client.js";
|
|
7
|
+
import type { Version, VersionRef } from "../kernel/wire.js";
|
|
8
|
+
import { type UserContent } from "./converge.js";
|
|
9
|
+
import type { FileStore } from "./reconcile.js";
|
|
10
|
+
import type { RemoteMap } from "./push.js";
|
|
11
|
+
/** Drop deferred entries older than this and force cold treatment (park/rebase). */
|
|
12
|
+
export declare const DEFER_TTL_MS: number;
|
|
13
|
+
export type DeferredEntry = {
|
|
14
|
+
ref: VersionRef;
|
|
15
|
+
since: number;
|
|
16
|
+
};
|
|
17
|
+
export type DeferredMap = Map<string, DeferredEntry>;
|
|
18
|
+
export type InboundDecision = {
|
|
19
|
+
action: "noop";
|
|
20
|
+
} | {
|
|
21
|
+
action: "adopt";
|
|
22
|
+
version: Version;
|
|
23
|
+
} | {
|
|
24
|
+
action: "materialize";
|
|
25
|
+
version: Version;
|
|
26
|
+
} | {
|
|
27
|
+
action: "rebase";
|
|
28
|
+
prevVersionId: string;
|
|
29
|
+
user: UserContent;
|
|
30
|
+
} | {
|
|
31
|
+
action: "defer";
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Pure mtime gate: true when the file was modified within settleMs of now.
|
|
35
|
+
* settleMs ≤ 0 disables the gate. Absent files (null mtime) are never hot.
|
|
36
|
+
*/
|
|
37
|
+
export declare function isPathHot(mtimeMs: number | null, settleMs: number, nowMs?: number): boolean;
|
|
38
|
+
/** Store-backed hot check. */
|
|
39
|
+
export declare function pathIsHot(store: FileStore, path: string, settleMs: number, nowMs?: number): Promise<boolean>;
|
|
40
|
+
/** True when the local embedded version is this ref or a later one. */
|
|
41
|
+
export declare function versionAtOrAhead(localVersion: string | undefined, refVersion: string): boolean;
|
|
42
|
+
export declare function isDeferExpired(entry: DeferredEntry, nowMs: number, ttlMs?: number): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Insert or replace a deferred hold. Newer version_id wins when both decode.
|
|
45
|
+
*/
|
|
46
|
+
export declare function enqueueDeferred(deferred: DeferredMap, path: string, ref: VersionRef, nowMs?: number): void;
|
|
47
|
+
/**
|
|
48
|
+
* Decide how to converge an existing local file with an incoming remote version.
|
|
49
|
+
* Does not touch the filesystem or kernel — callers run `effectInbound`.
|
|
50
|
+
*
|
|
51
|
+
* When `canDefer` is false (e.g. `--once` with no deferred map), hot files are
|
|
52
|
+
* treated as cold so the pass still converges.
|
|
53
|
+
*/
|
|
54
|
+
export declare function decideInbound(opts: {
|
|
55
|
+
localText: string;
|
|
56
|
+
remote: Version;
|
|
57
|
+
hot: boolean;
|
|
58
|
+
canDefer: boolean;
|
|
59
|
+
}): InboundDecision;
|
|
60
|
+
export type EffectResult = "noop" | "applied" | "deferred" | "parked";
|
|
61
|
+
/**
|
|
62
|
+
* Apply a decision from `decideInbound`. Rebase failures (`stale_prev` /
|
|
63
|
+
* `path_taken`) fall through to parking the remote as an ignored sibling.
|
|
64
|
+
*/
|
|
65
|
+
export declare function effectInbound(client: KernelClient, store: FileStore, repo: string, path: string, decision: InboundDecision, opts?: {
|
|
66
|
+
deferred?: DeferredMap;
|
|
67
|
+
ref?: VersionRef;
|
|
68
|
+
map?: RemoteMap;
|
|
69
|
+
nowMs?: number;
|
|
70
|
+
}): Promise<EffectResult>;
|
|
71
|
+
/**
|
|
72
|
+
* Retry one deferred hold: re-read local, fetch remote, decide, effect.
|
|
73
|
+
* Returns whether the entry should be removed from the deferred map.
|
|
74
|
+
*/
|
|
75
|
+
export declare function retryDeferredEntry(client: KernelClient, store: FileStore, repo: string, path: string, entry: DeferredEntry, opts: {
|
|
76
|
+
settleMs: number;
|
|
77
|
+
map?: RemoteMap;
|
|
78
|
+
nowMs?: number;
|
|
79
|
+
deferred?: DeferredMap;
|
|
80
|
+
}): Promise<{
|
|
81
|
+
done: boolean;
|
|
82
|
+
result: EffectResult;
|
|
83
|
+
}>;
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hot-path detection and inbound convergence decisions (better-sync.plan).
|
|
3
|
+
* Feed/reconcile call `decideInbound` then `effectInbound`; the daemon holds
|
|
4
|
+
* deferred refs in memory and retries them on later polls.
|
|
5
|
+
*/
|
|
6
|
+
import { KernelError } from "../kernel/errors.js";
|
|
7
|
+
import { decodeVersionId } from "../kernel/version-id.js";
|
|
8
|
+
import { materializeAt, parkIgnoredSibling, putAndAck, stripToUserContent, } from "./converge.js";
|
|
9
|
+
import { isDirty, isIgnored, readFileIntrinsics } from "./intrinsics.js";
|
|
10
|
+
/** Drop deferred entries older than this and force cold treatment (park/rebase). */
|
|
11
|
+
export const DEFER_TTL_MS = 30 * 60 * 1000;
|
|
12
|
+
/**
|
|
13
|
+
* Pure mtime gate: true when the file was modified within settleMs of now.
|
|
14
|
+
* settleMs ≤ 0 disables the gate. Absent files (null mtime) are never hot.
|
|
15
|
+
*/
|
|
16
|
+
export function isPathHot(mtimeMs, settleMs, nowMs = Date.now()) {
|
|
17
|
+
if (settleMs <= 0)
|
|
18
|
+
return false;
|
|
19
|
+
if (mtimeMs === null)
|
|
20
|
+
return false;
|
|
21
|
+
return nowMs - mtimeMs < settleMs;
|
|
22
|
+
}
|
|
23
|
+
/** Store-backed hot check. */
|
|
24
|
+
export async function pathIsHot(store, path, settleMs, nowMs = Date.now()) {
|
|
25
|
+
return isPathHot(await store.mtime(path), settleMs, nowMs);
|
|
26
|
+
}
|
|
27
|
+
/** True when the local embedded version is this ref or a later one. */
|
|
28
|
+
export function versionAtOrAhead(localVersion, refVersion) {
|
|
29
|
+
if (!localVersion)
|
|
30
|
+
return false;
|
|
31
|
+
if (localVersion === refVersion)
|
|
32
|
+
return true;
|
|
33
|
+
const local = decodeVersionId(localVersion);
|
|
34
|
+
const ref = decodeVersionId(refVersion);
|
|
35
|
+
if (local === null || ref === null)
|
|
36
|
+
return false;
|
|
37
|
+
return local > ref;
|
|
38
|
+
}
|
|
39
|
+
export function isDeferExpired(entry, nowMs, ttlMs = DEFER_TTL_MS) {
|
|
40
|
+
return nowMs - entry.since >= ttlMs;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Insert or replace a deferred hold. Newer version_id wins when both decode.
|
|
44
|
+
*/
|
|
45
|
+
export function enqueueDeferred(deferred, path, ref, nowMs = Date.now()) {
|
|
46
|
+
const existing = deferred.get(path);
|
|
47
|
+
if (existing) {
|
|
48
|
+
const old = decodeVersionId(existing.ref.version_id);
|
|
49
|
+
const neu = decodeVersionId(ref.version_id);
|
|
50
|
+
if (old !== null && neu !== null && neu <= old)
|
|
51
|
+
return;
|
|
52
|
+
// Keep original `since` so TTL is from first deferral of this path.
|
|
53
|
+
deferred.set(path, { ref, since: existing.since });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
deferred.set(path, { ref, since: nowMs });
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Decide how to converge an existing local file with an incoming remote version.
|
|
60
|
+
* Does not touch the filesystem or kernel — callers run `effectInbound`.
|
|
61
|
+
*
|
|
62
|
+
* When `canDefer` is false (e.g. `--once` with no deferred map), hot files are
|
|
63
|
+
* treated as cold so the pass still converges.
|
|
64
|
+
*/
|
|
65
|
+
export function decideInbound(opts) {
|
|
66
|
+
const { localText, remote } = opts;
|
|
67
|
+
const treatHot = opts.hot && opts.canDefer;
|
|
68
|
+
const intr = readFileIntrinsics(localText);
|
|
69
|
+
if (isIgnored(intr))
|
|
70
|
+
return { action: "noop" };
|
|
71
|
+
if (intr.computed_hash === remote.content_hash) {
|
|
72
|
+
if (intr.version === remote.version_id && intr.content_hash === remote.content_hash) {
|
|
73
|
+
return { action: "noop" };
|
|
74
|
+
}
|
|
75
|
+
if (treatHot)
|
|
76
|
+
return { action: "defer" };
|
|
77
|
+
return { action: "adopt", version: remote };
|
|
78
|
+
}
|
|
79
|
+
if (versionAtOrAhead(intr.version, remote.version_id)) {
|
|
80
|
+
return { action: "noop" };
|
|
81
|
+
}
|
|
82
|
+
if (!isDirty(intr)) {
|
|
83
|
+
if (treatHot)
|
|
84
|
+
return { action: "defer" };
|
|
85
|
+
return { action: "materialize", version: remote };
|
|
86
|
+
}
|
|
87
|
+
// Dirty + divergent: rebase local bytes onto remote current (better-sync WS2).
|
|
88
|
+
if (treatHot)
|
|
89
|
+
return { action: "defer" };
|
|
90
|
+
return {
|
|
91
|
+
action: "rebase",
|
|
92
|
+
prevVersionId: remote.version_id,
|
|
93
|
+
user: stripToUserContent(localText),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Apply a decision from `decideInbound`. Rebase failures (`stale_prev` /
|
|
98
|
+
* `path_taken`) fall through to parking the remote as an ignored sibling.
|
|
99
|
+
*/
|
|
100
|
+
export async function effectInbound(client, store, repo, path, decision, opts = {}) {
|
|
101
|
+
const nowMs = opts.nowMs ?? Date.now();
|
|
102
|
+
switch (decision.action) {
|
|
103
|
+
case "noop":
|
|
104
|
+
return "noop";
|
|
105
|
+
case "defer": {
|
|
106
|
+
if (opts.deferred && opts.ref) {
|
|
107
|
+
enqueueDeferred(opts.deferred, path, opts.ref, nowMs);
|
|
108
|
+
return "deferred";
|
|
109
|
+
}
|
|
110
|
+
// No place to hold it — treat as cold materialize of the ref's version.
|
|
111
|
+
return "noop";
|
|
112
|
+
}
|
|
113
|
+
case "adopt": {
|
|
114
|
+
await materializeAt(store, path, decision.version, { preserveMtime: true });
|
|
115
|
+
opts.map?.set(path, {
|
|
116
|
+
version_id: decision.version.version_id,
|
|
117
|
+
content_hash: decision.version.content_hash,
|
|
118
|
+
});
|
|
119
|
+
return "applied";
|
|
120
|
+
}
|
|
121
|
+
case "materialize": {
|
|
122
|
+
await materializeAt(store, path, decision.version);
|
|
123
|
+
opts.map?.set(path, {
|
|
124
|
+
version_id: decision.version.version_id,
|
|
125
|
+
content_hash: decision.version.content_hash,
|
|
126
|
+
});
|
|
127
|
+
return "applied";
|
|
128
|
+
}
|
|
129
|
+
case "rebase": {
|
|
130
|
+
try {
|
|
131
|
+
const v = await putAndAck(client, store, repo, path, decision.prevVersionId, decision.user);
|
|
132
|
+
opts.map?.set(path, { version_id: v.version_id, content_hash: v.content_hash });
|
|
133
|
+
return "applied";
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
if (err instanceof KernelError &&
|
|
137
|
+
(err.code === "stale_prev" || err.code === "path_taken")) {
|
|
138
|
+
const current = await client.docs.get_version(repo, decision.prevVersionId);
|
|
139
|
+
// Prefer live current at path if available (may have advanced).
|
|
140
|
+
let park = current;
|
|
141
|
+
try {
|
|
142
|
+
park = await client.docs.get(repo, path);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
if (!(err instanceof KernelError && err.code === "doc_not_found"))
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
await parkIgnoredSibling(store, path, park);
|
|
149
|
+
return "parked";
|
|
150
|
+
}
|
|
151
|
+
throw err;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Retry one deferred hold: re-read local, fetch remote, decide, effect.
|
|
158
|
+
* Returns whether the entry should be removed from the deferred map.
|
|
159
|
+
*/
|
|
160
|
+
export async function retryDeferredEntry(client, store, repo, path, entry, opts) {
|
|
161
|
+
const nowMs = opts.nowMs ?? Date.now();
|
|
162
|
+
const forceCold = isDeferExpired(entry, nowMs);
|
|
163
|
+
const hot = forceCold ? false : await pathIsHot(store, path, opts.settleMs, nowMs);
|
|
164
|
+
const canDefer = opts.deferred !== undefined && !forceCold;
|
|
165
|
+
if (entry.ref.op === "delete") {
|
|
166
|
+
const text = await store.read(path);
|
|
167
|
+
if (text === null)
|
|
168
|
+
return { done: true, result: "noop" };
|
|
169
|
+
const intr = readFileIntrinsics(text);
|
|
170
|
+
if (isIgnored(intr) || isDirty(intr))
|
|
171
|
+
return { done: true, result: "noop" };
|
|
172
|
+
if (hot && canDefer)
|
|
173
|
+
return { done: false, result: "deferred" };
|
|
174
|
+
await store.remove(path);
|
|
175
|
+
opts.map?.delete(path);
|
|
176
|
+
return { done: true, result: "applied" };
|
|
177
|
+
}
|
|
178
|
+
const text = await store.read(path);
|
|
179
|
+
if (text === null) {
|
|
180
|
+
// Local gone — materialize remote current if still live.
|
|
181
|
+
try {
|
|
182
|
+
const current = await client.docs.get(repo, path);
|
|
183
|
+
if (hot && canDefer)
|
|
184
|
+
return { done: false, result: "deferred" };
|
|
185
|
+
await materializeAt(store, path, current);
|
|
186
|
+
opts.map?.set(path, {
|
|
187
|
+
version_id: current.version_id,
|
|
188
|
+
content_hash: current.content_hash,
|
|
189
|
+
});
|
|
190
|
+
return { done: true, result: "applied" };
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
if (err instanceof KernelError && err.code === "doc_not_found") {
|
|
194
|
+
return { done: true, result: "noop" };
|
|
195
|
+
}
|
|
196
|
+
throw err;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Prefer live current at path; fall back to the held version.
|
|
200
|
+
let remote;
|
|
201
|
+
try {
|
|
202
|
+
remote = await client.docs.get(repo, path);
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
if (!(err instanceof KernelError && err.code === "doc_not_found"))
|
|
206
|
+
throw err;
|
|
207
|
+
try {
|
|
208
|
+
remote = await client.docs.get_version(repo, entry.ref.version_id);
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
if (err instanceof KernelError && err.code === "doc_not_found") {
|
|
212
|
+
return { done: true, result: "noop" };
|
|
213
|
+
}
|
|
214
|
+
throw err;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const decision = decideInbound({
|
|
218
|
+
localText: text,
|
|
219
|
+
remote,
|
|
220
|
+
hot,
|
|
221
|
+
canDefer,
|
|
222
|
+
});
|
|
223
|
+
if (decision.action === "defer") {
|
|
224
|
+
return { done: false, result: "deferred" };
|
|
225
|
+
}
|
|
226
|
+
const result = await effectInbound(client, store, repo, path, decision, {
|
|
227
|
+
map: opts.map,
|
|
228
|
+
nowMs,
|
|
229
|
+
});
|
|
230
|
+
return { done: true, result };
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=hot-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hot-path.js","sourceRoot":"","sources":["../../src/sync/hot-path.ts"],"names":[],"mappings":"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"}
|