mrplex 0.0.1

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.
Files changed (319) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +284 -0
  3. package/dist/cli/config.d.ts +28 -0
  4. package/dist/cli/config.js +48 -0
  5. package/dist/cli/config.js.map +1 -0
  6. package/dist/cli/exit-codes.d.ts +13 -0
  7. package/dist/cli/exit-codes.js +39 -0
  8. package/dist/cli/exit-codes.js.map +1 -0
  9. package/dist/cli/format.d.ts +22 -0
  10. package/dist/cli/format.js +148 -0
  11. package/dist/cli/format.js.map +1 -0
  12. package/dist/cli/main.d.ts +16 -0
  13. package/dist/cli/main.js +1518 -0
  14. package/dist/cli/main.js.map +1 -0
  15. package/dist/client/kernel-client.d.ts +97 -0
  16. package/dist/client/kernel-client.js +14 -0
  17. package/dist/client/kernel-client.js.map +1 -0
  18. package/dist/client/local.d.ts +26 -0
  19. package/dist/client/local.js +103 -0
  20. package/dist/client/local.js.map +1 -0
  21. package/dist/client/remote-mcp.d.ts +35 -0
  22. package/dist/client/remote-mcp.js +174 -0
  23. package/dist/client/remote-mcp.js.map +1 -0
  24. package/dist/embed/backfill.d.ts +24 -0
  25. package/dist/embed/backfill.js +33 -0
  26. package/dist/embed/backfill.js.map +1 -0
  27. package/dist/embed/chunker.d.ts +31 -0
  28. package/dist/embed/chunker.js +121 -0
  29. package/dist/embed/chunker.js.map +1 -0
  30. package/dist/embed/cmd-hook.d.ts +21 -0
  31. package/dist/embed/cmd-hook.js +100 -0
  32. package/dist/embed/cmd-hook.js.map +1 -0
  33. package/dist/embed/config.d.ts +61 -0
  34. package/dist/embed/config.js +109 -0
  35. package/dist/embed/config.js.map +1 -0
  36. package/dist/embed/hook.d.ts +38 -0
  37. package/dist/embed/hook.js +56 -0
  38. package/dist/embed/hook.js.map +1 -0
  39. package/dist/embed/http-hook.d.ts +13 -0
  40. package/dist/embed/http-hook.js +39 -0
  41. package/dist/embed/http-hook.js.map +1 -0
  42. package/dist/embed/worker.d.ts +62 -0
  43. package/dist/embed/worker.js +228 -0
  44. package/dist/embed/worker.js.map +1 -0
  45. package/dist/fixtures/hwe/characters/albion-ever.md +12 -0
  46. package/dist/fixtures/hwe/characters/cent.md +11 -0
  47. package/dist/fixtures/hwe/concepts/cent-demiurge-resonance.md +10 -0
  48. package/dist/fixtures/hwe/concepts/phase-looper.md +10 -0
  49. package/dist/fixtures/hwe/entities/demiurge.md +11 -0
  50. package/dist/fixtures/hwe/entities/monad.md +11 -0
  51. package/dist/fixtures/hwe/moc/globocorp-network.md +19 -0
  52. package/dist/fixtures/hwe/organizations/globocorp.md +11 -0
  53. package/dist/fixtures/hwe/readme.md +24 -0
  54. package/dist/fixtures/starship/crew/aria-okonkwo.md +14 -0
  55. package/dist/fixtures/starship/crew/bexley-orr.md +14 -0
  56. package/dist/fixtures/starship/crew/dax-thorne.md +14 -0
  57. package/dist/fixtures/starship/crew/isolde-marsh.md +16 -0
  58. package/dist/fixtures/starship/crew/kestrel-vance.md +15 -0
  59. package/dist/fixtures/starship/crew/quill-vasquez.md +14 -0
  60. package/dist/fixtures/starship/crew/soren-halloway.md +16 -0
  61. package/dist/fixtures/starship/encounters/the-drift-choir.md +14 -0
  62. package/dist/fixtures/starship/encounters/the-hollow-signal.md +16 -0
  63. package/dist/fixtures/starship/encounters/the-wandering-buoy.md +14 -0
  64. package/dist/fixtures/starship/equipment/coolant-loop-b.md +13 -0
  65. package/dist/fixtures/starship/equipment/emergency-ration-bars.md +12 -0
  66. package/dist/fixtures/starship/equipment/long-range-sensors.md +13 -0
  67. package/dist/fixtures/starship/equipment/plasma-manifold-3.md +15 -0
  68. package/dist/fixtures/starship/equipment/shuttle-corvid.md +13 -0
  69. package/dist/fixtures/starship/logs/marsh-4419-1.md +13 -0
  70. package/dist/fixtures/starship/logs/okonkwo-4415-1.md +13 -0
  71. package/dist/fixtures/starship/logs/orr-4413-1.md +13 -0
  72. package/dist/fixtures/starship/logs/orr-4420-1.md +12 -0
  73. package/dist/fixtures/starship/logs/thorne-4413-2.md +13 -0
  74. package/dist/fixtures/starship/logs/vance-4419-2.md +13 -0
  75. package/dist/fixtures/starship/misc/the-galley-ficus.md +11 -0
  76. package/dist/fixtures/starship/missions/the-cinder-run.md +17 -0
  77. package/dist/fixtures/starship/missions/the-drift-choir.md +17 -0
  78. package/dist/fixtures/starship/missions/the-hollow-signal.md +17 -0
  79. package/dist/fixtures/starship/missions/the-silent-beacon.md +17 -0
  80. package/dist/fixtures/starship/moc/crew.md +19 -0
  81. package/dist/fixtures/starship/moc/encounters.md +14 -0
  82. package/dist/fixtures/starship/moc/missions.md +15 -0
  83. package/dist/fixtures/starship/readme.md +60 -0
  84. package/dist/index.d.ts +1 -0
  85. package/dist/index.js +3 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/kernel/auth/glob.d.ts +46 -0
  88. package/dist/kernel/auth/glob.js +112 -0
  89. package/dist/kernel/auth/glob.js.map +1 -0
  90. package/dist/kernel/auth/scope.d.ts +53 -0
  91. package/dist/kernel/auth/scope.js +83 -0
  92. package/dist/kernel/auth/scope.js.map +1 -0
  93. package/dist/kernel/casefold.d.ts +37 -0
  94. package/dist/kernel/casefold.js +44 -0
  95. package/dist/kernel/casefold.js.map +1 -0
  96. package/dist/kernel/constants.d.ts +21 -0
  97. package/dist/kernel/constants.js +22 -0
  98. package/dist/kernel/constants.js.map +1 -0
  99. package/dist/kernel/context.d.ts +70 -0
  100. package/dist/kernel/context.js +114 -0
  101. package/dist/kernel/context.js.map +1 -0
  102. package/dist/kernel/deletion.d.ts +50 -0
  103. package/dist/kernel/deletion.js +83 -0
  104. package/dist/kernel/deletion.js.map +1 -0
  105. package/dist/kernel/diff.d.ts +36 -0
  106. package/dist/kernel/diff.js +61 -0
  107. package/dist/kernel/diff.js.map +1 -0
  108. package/dist/kernel/errors.d.ts +37 -0
  109. package/dist/kernel/errors.js +55 -0
  110. package/dist/kernel/errors.js.map +1 -0
  111. package/dist/kernel/frontmatter-input.d.ts +32 -0
  112. package/dist/kernel/frontmatter-input.js +85 -0
  113. package/dist/kernel/frontmatter-input.js.map +1 -0
  114. package/dist/kernel/graph.d.ts +34 -0
  115. package/dist/kernel/graph.js +559 -0
  116. package/dist/kernel/graph.js.map +1 -0
  117. package/dist/kernel/history.d.ts +74 -0
  118. package/dist/kernel/history.js +143 -0
  119. package/dist/kernel/history.js.map +1 -0
  120. package/dist/kernel/kernel.d.ts +162 -0
  121. package/dist/kernel/kernel.js +675 -0
  122. package/dist/kernel/kernel.js.map +1 -0
  123. package/dist/kernel/path-config.d.ts +101 -0
  124. package/dist/kernel/path-config.js +171 -0
  125. package/dist/kernel/path-config.js.map +1 -0
  126. package/dist/kernel/query/ast.d.ts +24 -0
  127. package/dist/kernel/query/ast.js +52 -0
  128. package/dist/kernel/query/ast.js.map +1 -0
  129. package/dist/kernel/query/cel-parse.d.ts +37 -0
  130. package/dist/kernel/query/cel-parse.js +88 -0
  131. package/dist/kernel/query/cel-parse.js.map +1 -0
  132. package/dist/kernel/query/degrees.d.ts +31 -0
  133. package/dist/kernel/query/degrees.js +135 -0
  134. package/dist/kernel/query/degrees.js.map +1 -0
  135. package/dist/kernel/query/graph-ast.d.ts +51 -0
  136. package/dist/kernel/query/graph-ast.js +139 -0
  137. package/dist/kernel/query/graph-ast.js.map +1 -0
  138. package/dist/kernel/query/query.d.ts +56 -0
  139. package/dist/kernel/query/query.js +276 -0
  140. package/dist/kernel/query/query.js.map +1 -0
  141. package/dist/kernel/validation.d.ts +64 -0
  142. package/dist/kernel/validation.js +183 -0
  143. package/dist/kernel/validation.js.map +1 -0
  144. package/dist/kernel/version-id.d.ts +10 -0
  145. package/dist/kernel/version-id.js +21 -0
  146. package/dist/kernel/version-id.js.map +1 -0
  147. package/dist/kernel/wire.d.ts +148 -0
  148. package/dist/kernel/wire.js +6 -0
  149. package/dist/kernel/wire.js.map +1 -0
  150. package/dist/links/backfill.d.ts +28 -0
  151. package/dist/links/backfill.js +33 -0
  152. package/dist/links/backfill.js.map +1 -0
  153. package/dist/links/extract.d.ts +29 -0
  154. package/dist/links/extract.js +233 -0
  155. package/dist/links/extract.js.map +1 -0
  156. package/dist/links/link-config.d.ts +70 -0
  157. package/dist/links/link-config.js +107 -0
  158. package/dist/links/link-config.js.map +1 -0
  159. package/dist/links/maintain.d.ts +37 -0
  160. package/dist/links/maintain.js +91 -0
  161. package/dist/links/maintain.js.map +1 -0
  162. package/dist/links/repair.d.ts +46 -0
  163. package/dist/links/repair.js +96 -0
  164. package/dist/links/repair.js.map +1 -0
  165. package/dist/links/resolve.d.ts +41 -0
  166. package/dist/links/resolve.js +121 -0
  167. package/dist/links/resolve.js.map +1 -0
  168. package/dist/links/stale.d.ts +32 -0
  169. package/dist/links/stale.js +73 -0
  170. package/dist/links/stale.js.map +1 -0
  171. package/dist/markdown/content-hash.d.ts +34 -0
  172. package/dist/markdown/content-hash.js +46 -0
  173. package/dist/markdown/content-hash.js.map +1 -0
  174. package/dist/markdown/frontmatter.d.ts +69 -0
  175. package/dist/markdown/frontmatter.js +147 -0
  176. package/dist/markdown/frontmatter.js.map +1 -0
  177. package/dist/markdown/hash-backfill.d.ts +22 -0
  178. package/dist/markdown/hash-backfill.js +43 -0
  179. package/dist/markdown/hash-backfill.js.map +1 -0
  180. package/dist/mcp/query-syntax.d.ts +20 -0
  181. package/dist/mcp/query-syntax.js +194 -0
  182. package/dist/mcp/query-syntax.js.map +1 -0
  183. package/dist/mcp/render.d.ts +31 -0
  184. package/dist/mcp/render.js +110 -0
  185. package/dist/mcp/render.js.map +1 -0
  186. package/dist/mcp/server.d.ts +60 -0
  187. package/dist/mcp/server.js +170 -0
  188. package/dist/mcp/server.js.map +1 -0
  189. package/dist/mcp/tools.d.ts +93 -0
  190. package/dist/mcp/tools.js +1218 -0
  191. package/dist/mcp/tools.js.map +1 -0
  192. package/dist/rest/conditional.d.ts +37 -0
  193. package/dist/rest/conditional.js +58 -0
  194. package/dist/rest/conditional.js.map +1 -0
  195. package/dist/rest/negotiate.d.ts +33 -0
  196. package/dist/rest/negotiate.js +54 -0
  197. package/dist/rest/negotiate.js.map +1 -0
  198. package/dist/rest/routes.d.ts +37 -0
  199. package/dist/rest/routes.js +744 -0
  200. package/dist/rest/routes.js.map +1 -0
  201. package/dist/seed/fixture-seed.d.ts +35 -0
  202. package/dist/seed/fixture-seed.js +75 -0
  203. package/dist/seed/fixture-seed.js.map +1 -0
  204. package/dist/seed/starship-config.d.ts +3 -0
  205. package/dist/seed/starship-config.js +5 -0
  206. package/dist/seed/starship-config.js.map +1 -0
  207. package/dist/server/headers.d.ts +36 -0
  208. package/dist/server/headers.js +30 -0
  209. package/dist/server/headers.js.map +1 -0
  210. package/dist/server/http-error.d.ts +53 -0
  211. package/dist/server/http-error.js +104 -0
  212. package/dist/server/http-error.js.map +1 -0
  213. package/dist/server/serve.d.ts +56 -0
  214. package/dist/server/serve.js +135 -0
  215. package/dist/server/serve.js.map +1 -0
  216. package/dist/shell/audit.d.ts +32 -0
  217. package/dist/shell/audit.js +28 -0
  218. package/dist/shell/audit.js.map +1 -0
  219. package/dist/shell/guard.d.ts +50 -0
  220. package/dist/shell/guard.js +176 -0
  221. package/dist/shell/guard.js.map +1 -0
  222. package/dist/shell/keys.d.ts +36 -0
  223. package/dist/shell/keys.js +64 -0
  224. package/dist/shell/keys.js.map +1 -0
  225. package/dist/shell/login.d.ts +56 -0
  226. package/dist/shell/login.js +130 -0
  227. package/dist/shell/login.js.map +1 -0
  228. package/dist/shell/oidc.d.ts +62 -0
  229. package/dist/shell/oidc.js +102 -0
  230. package/dist/shell/oidc.js.map +1 -0
  231. package/dist/shell/policy.d.ts +100 -0
  232. package/dist/shell/policy.js +232 -0
  233. package/dist/shell/policy.js.map +1 -0
  234. package/dist/shell/proxy-policy.d.ts +34 -0
  235. package/dist/shell/proxy-policy.js +120 -0
  236. package/dist/shell/proxy-policy.js.map +1 -0
  237. package/dist/shell/proxy.d.ts +47 -0
  238. package/dist/shell/proxy.js +253 -0
  239. package/dist/shell/proxy.js.map +1 -0
  240. package/dist/shell/serve.d.ts +86 -0
  241. package/dist/shell/serve.js +0 -0
  242. package/dist/shell/serve.js.map +1 -0
  243. package/dist/shell/stdio.d.ts +55 -0
  244. package/dist/shell/stdio.js +63 -0
  245. package/dist/shell/stdio.js.map +1 -0
  246. package/dist/storage/registry.d.ts +20 -0
  247. package/dist/storage/registry.js +38 -0
  248. package/dist/storage/registry.js.map +1 -0
  249. package/dist/storage/search-plan.d.ts +93 -0
  250. package/dist/storage/search-plan.js +15 -0
  251. package/dist/storage/search-plan.js.map +1 -0
  252. package/dist/storage/types.d.ts +448 -0
  253. package/dist/storage/types.js +14 -0
  254. package/dist/storage/types.js.map +1 -0
  255. package/dist/storage/versions-since.d.ts +72 -0
  256. package/dist/storage/versions-since.js +92 -0
  257. package/dist/storage/versions-since.js.map +1 -0
  258. package/dist/storage-postgres/adapter.d.ts +17 -0
  259. package/dist/storage-postgres/adapter.js +782 -0
  260. package/dist/storage-postgres/adapter.js.map +1 -0
  261. package/dist/storage-postgres/compile-postgres.d.ts +22 -0
  262. package/dist/storage-postgres/compile-postgres.js +830 -0
  263. package/dist/storage-postgres/compile-postgres.js.map +1 -0
  264. package/dist/storage-postgres/errors.d.ts +34 -0
  265. package/dist/storage-postgres/errors.js +50 -0
  266. package/dist/storage-postgres/errors.js.map +1 -0
  267. package/dist/storage-postgres/migrations/0001_init.sql +113 -0
  268. package/dist/storage-postgres/migrations/0002_content_hash.sql +7 -0
  269. package/dist/storage-postgres/migrations/index.d.ts +22 -0
  270. package/dist/storage-postgres/migrations/index.js +70 -0
  271. package/dist/storage-postgres/migrations/index.js.map +1 -0
  272. package/dist/storage-sqlite/adapter.d.ts +2 -0
  273. package/dist/storage-sqlite/adapter.js +688 -0
  274. package/dist/storage-sqlite/adapter.js.map +1 -0
  275. package/dist/storage-sqlite/compile-filter.d.ts +41 -0
  276. package/dist/storage-sqlite/compile-filter.js +881 -0
  277. package/dist/storage-sqlite/compile-filter.js.map +1 -0
  278. package/dist/storage-sqlite/compile-sqlite.d.ts +19 -0
  279. package/dist/storage-sqlite/compile-sqlite.js +176 -0
  280. package/dist/storage-sqlite/compile-sqlite.js.map +1 -0
  281. package/dist/storage-sqlite/migrations/0001_init.sql +123 -0
  282. package/dist/storage-sqlite/migrations/0002_content_hash.sql +7 -0
  283. package/dist/storage-sqlite/migrations/index.d.ts +6 -0
  284. package/dist/storage-sqlite/migrations/index.js +41 -0
  285. package/dist/storage-sqlite/migrations/index.js.map +1 -0
  286. package/dist/storage-sqlite/vec.d.ts +40 -0
  287. package/dist/storage-sqlite/vec.js +62 -0
  288. package/dist/storage-sqlite/vec.js.map +1 -0
  289. package/dist/sync/cursor.d.ts +36 -0
  290. package/dist/sync/cursor.js +66 -0
  291. package/dist/sync/cursor.js.map +1 -0
  292. package/dist/sync/daemon.d.ts +40 -0
  293. package/dist/sync/daemon.js +202 -0
  294. package/dist/sync/daemon.js.map +1 -0
  295. package/dist/sync/feed.d.ts +38 -0
  296. package/dist/sync/feed.js +125 -0
  297. package/dist/sync/feed.js.map +1 -0
  298. package/dist/sync/fs-store.d.ts +8 -0
  299. package/dist/sync/fs-store.js +76 -0
  300. package/dist/sync/fs-store.js.map +1 -0
  301. package/dist/sync/intrinsics.d.ts +52 -0
  302. package/dist/sync/intrinsics.js +76 -0
  303. package/dist/sync/intrinsics.js.map +1 -0
  304. package/dist/sync/paths.d.ts +38 -0
  305. package/dist/sync/paths.js +56 -0
  306. package/dist/sync/paths.js.map +1 -0
  307. package/dist/sync/push.d.ts +48 -0
  308. package/dist/sync/push.js +328 -0
  309. package/dist/sync/push.js.map +1 -0
  310. package/dist/sync/reconcile.d.ts +54 -0
  311. package/dist/sync/reconcile.js +252 -0
  312. package/dist/sync/reconcile.js.map +1 -0
  313. package/dist/sync/run.d.ts +27 -0
  314. package/dist/sync/run.js +46 -0
  315. package/dist/sync/run.js.map +1 -0
  316. package/dist/version.d.ts +2 -0
  317. package/dist/version.js +5 -0
  318. package/dist/version.js.map +1 -0
  319. package/package.json +80 -0
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Reading and writing the self-describing frontmatter that makes sync work
3
+ * (sync/history plan §2.4, §4.2). A materialized file embeds `$version` and
4
+ * `$content_hash`; a user may also add the `$sync: ignore` client directive.
5
+ * Per-file sync state lives *in the file* — this module is the single place
6
+ * that parses it out and renders it back.
7
+ */
8
+ import { contentHashOfFile } from "../markdown/content-hash.js";
9
+ import { appendSystemProperty, extractSystemProperties, join, split, } from "../markdown/frontmatter.js";
10
+ /** The `$sync` directive value we honor (§4.2). Only `ignore` is defined. */
11
+ export const SYNC_IGNORE = "ignore";
12
+ /**
13
+ * Parse a file's embedded intrinsics and compute its canonical content hash.
14
+ * The computed hash excludes all `$*` lines, so adding/removing intrinsics or
15
+ * the `$sync` directive never changes it (§4.2).
16
+ */
17
+ export function readFileIntrinsics(text) {
18
+ const lf = text.replace(/\r\n/g, "\n");
19
+ const { frontmatter_raw } = split(lf);
20
+ const { props } = extractSystemProperties(frontmatter_raw);
21
+ return {
22
+ version: props.version,
23
+ content_hash: props.content_hash,
24
+ sync: props.sync,
25
+ computed_hash: contentHashOfFile(lf),
26
+ };
27
+ }
28
+ /** True when the file opts out of sync via `$sync: ignore` (§4.2). */
29
+ export function isIgnored(intr) {
30
+ return intr.sync === SYNC_IGNORE;
31
+ }
32
+ /**
33
+ * A file is "dirty" when its user bytes differ from the version its embedded
34
+ * `$version` names — i.e. the computed hash ≠ the embedded `$content_hash`
35
+ * (§4.4 hash gate). A file with no embedded hash is treated as dirty (we
36
+ * cannot prove it clean).
37
+ */
38
+ export function isDirty(intr) {
39
+ return intr.content_hash === undefined || intr.computed_hash !== intr.content_hash;
40
+ }
41
+ /**
42
+ * Render a version as a self-describing file: strip any stale `$*` lines from
43
+ * the stored frontmatter, then append `$version` and `$content_hash` in fixed
44
+ * order (§2.4). Robust whether or not the transport already injected them.
45
+ */
46
+ export function renderMaterialized(v) {
47
+ const { raw } = extractSystemProperties(v.frontmatter_raw);
48
+ let fm = appendSystemProperty(raw, "version", v.version_id);
49
+ fm = appendSystemProperty(fm, "content_hash", v.content_hash);
50
+ return join({ frontmatter_raw: fm, body: v.body });
51
+ }
52
+ /**
53
+ * Render a version as a conflict sibling (§4.8): the normal materialization
54
+ * plus `$sync: ignore`, which is the *only* thing keeping it out of sync.
55
+ */
56
+ export function renderIgnoredSibling(v) {
57
+ const { raw } = extractSystemProperties(v.frontmatter_raw);
58
+ let fm = appendSystemProperty(raw, "version", v.version_id);
59
+ fm = appendSystemProperty(fm, "content_hash", v.content_hash);
60
+ fm = appendSystemProperty(fm, "sync", SYNC_IGNORE);
61
+ return join({ frontmatter_raw: fm, body: v.body });
62
+ }
63
+ /**
64
+ * Stamp `$version` / `$content_hash` onto an existing file without replacing
65
+ * its user bytes. Used when the editor saved again between our kernel write
66
+ * and the ack rewrite — we must not revert their typing.
67
+ */
68
+ export function stampProvenance(text, versionId, contentHash) {
69
+ const lf = text.replace(/\r\n/g, "\n");
70
+ const { frontmatter_raw, body } = split(lf);
71
+ const { raw } = extractSystemProperties(frontmatter_raw);
72
+ let fm = appendSystemProperty(raw, "version", versionId);
73
+ fm = appendSystemProperty(fm, "content_hash", contentHash);
74
+ return join({ frontmatter_raw: fm, body });
75
+ }
76
+ //# sourceMappingURL=intrinsics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intrinsics.js","sourceRoot":"","sources":["../../src/sync/intrinsics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Path mapping and scope filtering for sync (sync/history plan §4.1). A single
3
+ * include/exclude predicate filters BOTH directions — the local walk, the index
4
+ * items, and the feed refs all pass through it — so the two sides can never
5
+ * disagree about what is in scope. Local `<root>`-relative paths map to doc
6
+ * paths by relative POSIX path.
7
+ */
8
+ /** The cursor/state directory inside a synced vault (§4.2). */
9
+ export declare const SYNC_DIR = ".mrplex";
10
+ /** The cursor file path relative to the vault root. */
11
+ export declare const CURSOR_FILE = ".mrplex/sync.json";
12
+ export type ScopeFilter = {
13
+ /** True when a doc path (POSIX, repo-relative) is in sync scope. */
14
+ matches(docPath: string): boolean;
15
+ };
16
+ /**
17
+ * Build the include/exclude predicate. Defaults to `**\/*.md` include (§4.1).
18
+ * Exclusions always win. Any path with a dot-prefixed segment is excluded
19
+ * unconditionally — this covers the `.mrplex/` state dir AND app dotdirs like
20
+ * `.obsidian/` (§4.2). Crucially, the local walk (fs-store) prunes the same
21
+ * dotdirs, so both directions filter identically and can never disagree about
22
+ * what is in scope (§4.1: "the same include/exclude globs filter both
23
+ * directions").
24
+ */
25
+ export declare function makeScopeFilter(opts?: {
26
+ include?: string[];
27
+ exclude?: string[];
28
+ }): ScopeFilter;
29
+ /** True when any POSIX path segment starts with a dot (`.mrplex/`, `.obsidian/`, …). */
30
+ export declare function hasDotSegment(docPath: string): boolean;
31
+ /**
32
+ * Map an absolute local file path to its repo-relative POSIX doc path, or null
33
+ * when the file is outside `<root>`. On POSIX this is just the relative path;
34
+ * on Windows backslashes fold to `/` so doc paths are portable.
35
+ */
36
+ export declare function toDocPath(root: string, absPath: string): string | null;
37
+ /** Map a repo-relative POSIX doc path back to an absolute local file path. */
38
+ export declare function toLocalPath(root: string, docPath: string): string;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Path mapping and scope filtering for sync (sync/history plan §4.1). A single
3
+ * include/exclude predicate filters BOTH directions — the local walk, the index
4
+ * items, and the feed refs all pass through it — so the two sides can never
5
+ * disagree about what is in scope. Local `<root>`-relative paths map to doc
6
+ * paths by relative POSIX path.
7
+ */
8
+ import { relative, resolve, sep } from "node:path";
9
+ import { pathMatchesGlobs } from "../kernel/auth/glob.js";
10
+ /** The cursor/state directory inside a synced vault (§4.2). */
11
+ export const SYNC_DIR = ".mrplex";
12
+ /** The cursor file path relative to the vault root. */
13
+ export const CURSOR_FILE = `${SYNC_DIR}/sync.json`;
14
+ /**
15
+ * Build the include/exclude predicate. Defaults to `**\/*.md` include (§4.1).
16
+ * Exclusions always win. Any path with a dot-prefixed segment is excluded
17
+ * unconditionally — this covers the `.mrplex/` state dir AND app dotdirs like
18
+ * `.obsidian/` (§4.2). Crucially, the local walk (fs-store) prunes the same
19
+ * dotdirs, so both directions filter identically and can never disagree about
20
+ * what is in scope (§4.1: "the same include/exclude globs filter both
21
+ * directions").
22
+ */
23
+ export function makeScopeFilter(opts) {
24
+ const include = opts?.include && opts.include.length > 0 ? opts.include : ["**/*.md"];
25
+ const exclude = opts?.exclude ?? [];
26
+ return {
27
+ matches(docPath) {
28
+ if (hasDotSegment(docPath))
29
+ return false;
30
+ if (exclude.length > 0 && pathMatchesGlobs(exclude, docPath))
31
+ return false;
32
+ return pathMatchesGlobs(include, docPath);
33
+ },
34
+ };
35
+ }
36
+ /** True when any POSIX path segment starts with a dot (`.mrplex/`, `.obsidian/`, …). */
37
+ export function hasDotSegment(docPath) {
38
+ return docPath.split("/").some((seg) => seg.startsWith("."));
39
+ }
40
+ /**
41
+ * Map an absolute local file path to its repo-relative POSIX doc path, or null
42
+ * when the file is outside `<root>`. On POSIX this is just the relative path;
43
+ * on Windows backslashes fold to `/` so doc paths are portable.
44
+ */
45
+ export function toDocPath(root, absPath) {
46
+ const rel = relative(resolve(root), resolve(absPath));
47
+ if (rel === "" || rel.startsWith("..") || rel.startsWith(`..${sep}`))
48
+ return null;
49
+ return sep === "/" ? rel : rel.split(sep).join("/");
50
+ }
51
+ /** Map a repo-relative POSIX doc path back to an absolute local file path. */
52
+ export function toLocalPath(root, docPath) {
53
+ const native = sep === "/" ? docPath : docPath.split("/").join(sep);
54
+ return resolve(root, native);
55
+ }
56
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/sync/paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,+DAA+D;AAC/D,MAAM,CAAC,MAAM,QAAQ,GAAG,SAAS,CAAC;AAClC,uDAAuD;AACvD,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,QAAQ,YAAY,CAAC;AAOnD;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,IAG/B;IACC,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACtF,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC;IACpC,OAAO;QACL,OAAO,CAAC,OAAe;YACrB,IAAI,aAAa,CAAC,OAAO,CAAC;gBAAE,OAAO,KAAK,CAAC;YACzC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC;gBAAE,OAAO,KAAK,CAAC;YAC3E,OAAO,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC5C,CAAC;KACF,CAAC;AACJ,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,OAAO,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,OAAe;IACrD,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IAClF,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,OAAe;IACvD,MAAM,MAAM,GAAG,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpE,OAAO,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Local → remote: the debounced per-path pass (sync/history plan §4.4, §4.6,
3
+ * §4.7). Given a settled path, decide: skip (ignored/clean), push (edit/create/
4
+ * move), witnessed-delete, or conflict-park. Event *types* are untrusted — the
5
+ * pass stats the path itself (§4.6): present → change; ENOENT → witnessed
6
+ * delete.
7
+ *
8
+ * The in-memory map (path → last-known { version_id, content_hash }) is the
9
+ * daemon's working knowledge, chiefly so a witnessed unlink still knows the
10
+ * prev_version_id of a file that no longer exists to be read (§4.2 tier 3).
11
+ *
12
+ * A rename is unlink(old) + add(new) of a file that still embeds `$version`.
13
+ * `pushBurst` is the §4.7 debounce burst: present paths (moves/edits) run
14
+ * before absent ones (deletes), and a delete is suppressed when another file
15
+ * in the burst still carries that version. `pushPath` itself still pushes a
16
+ * *clean* move when the map knows the version at a different path, and a
17
+ * late-arriving dest after a premature delete restores from `:deleted`
18
+ * rather than parking a no-op conflict.
19
+ */
20
+ import type { KernelClient } from "../client/kernel-client.js";
21
+ import type { FileStore } from "./reconcile.js";
22
+ /** Last-known remote state per path (§4.2 tier 3). */
23
+ export type RemoteMap = Map<string, {
24
+ version_id: string;
25
+ content_hash: string;
26
+ }>;
27
+ export type PushResult = "skip" | "clean" | "ignored" | "created" | "updated" | "deleted" | "conflict" | "no-op-untracked-unlink";
28
+ export type PushDeps = {
29
+ client: KernelClient;
30
+ store: FileStore;
31
+ repo: string;
32
+ map: RemoteMap;
33
+ log?: (msg: string) => void;
34
+ };
35
+ /**
36
+ * Process a debounce burst of paths together (§4.7). Present files (moves,
37
+ * edits, creates) run first so a rename's dest put supersedes the source
38
+ * unlink; a witnessed delete is skipped when another file in the burst still
39
+ * embeds that version. Each path is isolated: one failure does not abort the
40
+ * rest.
41
+ */
42
+ export declare function pushBurst(paths: string[], deps: PushDeps): Promise<PushResult[]>;
43
+ /**
44
+ * Process one settled path. The store's `read` returning null is the stat
45
+ * verdict "absent" (a witnessed delete); a non-null read is "present" (a
46
+ * change), coalescing the atomic-rename save case into a plain update.
47
+ */
48
+ export declare function pushPath(path: string, deps: PushDeps): Promise<PushResult>;
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Local → remote: the debounced per-path pass (sync/history plan §4.4, §4.6,
3
+ * §4.7). Given a settled path, decide: skip (ignored/clean), push (edit/create/
4
+ * move), witnessed-delete, or conflict-park. Event *types* are untrusted — the
5
+ * pass stats the path itself (§4.6): present → change; ENOENT → witnessed
6
+ * delete.
7
+ *
8
+ * The in-memory map (path → last-known { version_id, content_hash }) is the
9
+ * daemon's working knowledge, chiefly so a witnessed unlink still knows the
10
+ * prev_version_id of a file that no longer exists to be read (§4.2 tier 3).
11
+ *
12
+ * A rename is unlink(old) + add(new) of a file that still embeds `$version`.
13
+ * `pushBurst` is the §4.7 debounce burst: present paths (moves/edits) run
14
+ * before absent ones (deletes), and a delete is suppressed when another file
15
+ * in the burst still carries that version. `pushPath` itself still pushes a
16
+ * *clean* move when the map knows the version at a different path, and a
17
+ * late-arriving dest after a premature delete restores from `:deleted`
18
+ * rather than parking a no-op conflict.
19
+ */
20
+ import { pathIsInSystemNamespace, withVersionSuffix } from "../kernel/deletion.js";
21
+ import { KernelError } from "../kernel/errors.js";
22
+ import { HARDCODED_DEFAULTS } from "../kernel/path-config.js";
23
+ import { extractSystemProperties, split } from "../markdown/frontmatter.js";
24
+ import { isDirty, isIgnored, readFileIntrinsics, renderIgnoredSibling, renderMaterialized, stampProvenance, } from "./intrinsics.js";
25
+ /**
26
+ * Process a debounce burst of paths together (§4.7). Present files (moves,
27
+ * edits, creates) run first so a rename's dest put supersedes the source
28
+ * unlink; a witnessed delete is skipped when another file in the burst still
29
+ * embeds that version. Each path is isolated: one failure does not abort the
30
+ * rest.
31
+ */
32
+ export async function pushBurst(paths, deps) {
33
+ const unique = [...new Set(paths)];
34
+ const present = [];
35
+ const absent = [];
36
+ const presentByVersion = new Map();
37
+ for (const path of unique) {
38
+ const text = await deps.store.read(path);
39
+ if (text === null) {
40
+ absent.push(path);
41
+ continue;
42
+ }
43
+ present.push(path);
44
+ const version = readFileIntrinsics(text).version;
45
+ if (version)
46
+ presentByVersion.set(version, path);
47
+ }
48
+ const suppressDelete = new Set();
49
+ for (const path of absent) {
50
+ const known = deps.map.get(path);
51
+ if (!known)
52
+ continue;
53
+ const dest = presentByVersion.get(known.version_id);
54
+ if (dest !== undefined && dest !== path)
55
+ suppressDelete.add(path);
56
+ }
57
+ const results = [];
58
+ const log = deps.log ?? (() => { });
59
+ for (const path of present) {
60
+ try {
61
+ results.push(await pushPath(path, deps));
62
+ }
63
+ catch (err) {
64
+ log(`push error\t${path}\t${err.message}`);
65
+ }
66
+ }
67
+ for (const path of absent) {
68
+ if (suppressDelete.has(path)) {
69
+ deps.map.delete(path);
70
+ results.push("no-op-untracked-unlink");
71
+ continue;
72
+ }
73
+ try {
74
+ results.push(await pushPath(path, deps));
75
+ }
76
+ catch (err) {
77
+ log(`push error\t${path}\t${err.message}`);
78
+ }
79
+ }
80
+ return results;
81
+ }
82
+ /**
83
+ * Process one settled path. The store's `read` returning null is the stat
84
+ * verdict "absent" (a witnessed delete); a non-null read is "present" (a
85
+ * change), coalescing the atomic-rename save case into a plain update.
86
+ */
87
+ export async function pushPath(path, deps) {
88
+ const { client, store, repo, map } = deps;
89
+ const log = deps.log ?? (() => { });
90
+ const text = await store.read(path);
91
+ // --- Witnessed delete (§4.6): the file is gone. ---
92
+ if (text === null) {
93
+ const known = map.get(path);
94
+ if (!known)
95
+ return "no-op-untracked-unlink"; // never tracked (e.g. $sync:ignore)
96
+ try {
97
+ await client.docs.delete(repo, known.version_id);
98
+ map.delete(path);
99
+ log(`delete\t${path}`);
100
+ return "deleted";
101
+ }
102
+ catch (err) {
103
+ if (err instanceof KernelError && err.code === "stale_prev") {
104
+ // Remote advanced after our last knowledge → skip the delete; the feed
105
+ // will re-materialize the newer version (§4.6 "remote edit outlives a
106
+ // local delete"). Drop our stale map entry so the feed can repopulate.
107
+ map.delete(path);
108
+ return "skip";
109
+ }
110
+ throw err;
111
+ }
112
+ }
113
+ const intr = readFileIntrinsics(text);
114
+ if (isIgnored(intr)) {
115
+ map.delete(path); // invisible to diffing + deletion (§4.4 directive gate)
116
+ return "ignored";
117
+ }
118
+ // Hash gate (§4.4): computed == embedded → clean, no push. Absorbs editor
119
+ // noise, our own materializations, and our own post-push intrinsic rewrites.
120
+ // Exceptions that still fall through to put:
121
+ // • `$version` is known at a *different* map path — a live rename (§4.7)
122
+ // • `$version` is not in the map at all — a late rename dest after the
123
+ // source was already dropped; currentAtPath equal to embedded is the
124
+ // only cheap "already synced here" proof that avoids a no-op version
125
+ if (!isDirty(intr)) {
126
+ const source = intr.version ? findMapPathByVersion(map, intr.version) : undefined;
127
+ if (source === path) {
128
+ return "clean";
129
+ }
130
+ if (source === undefined) {
131
+ if (intr.version) {
132
+ const current = await currentAtPath(client, repo, path);
133
+ if (current && current.version_id === intr.version) {
134
+ map.set(path, { version_id: current.version_id, content_hash: current.content_hash });
135
+ return "clean";
136
+ }
137
+ // dest empty or different version → fall through to put / restore
138
+ }
139
+ else {
140
+ return "clean";
141
+ }
142
+ }
143
+ }
144
+ const user = stripToUserContent(text);
145
+ // Provenance present → optimistic put (§4.4). This is also how a move
146
+ // propagates (§4.7): the file at the new path still embeds its `$version`,
147
+ // and a put whose path differs from the prev version's path IS a move that
148
+ // preserves document identity — the kernel resolves it from prev_version_id.
149
+ if (intr.version) {
150
+ try {
151
+ return await commitPut(deps, path, intr.version, user, "update");
152
+ }
153
+ catch (err) {
154
+ if (err instanceof KernelError && err.code === "stale_prev") {
155
+ return recoverStalePut(deps, path, user, intr.computed_hash, err);
156
+ }
157
+ throw err;
158
+ }
159
+ }
160
+ // Dirty, no embedded provenance → resolve the current remote doc at this path
161
+ // first (§4.4): absent → create; present + hash-equal → adopt provenance in
162
+ // place; present + differ → occupied-path conflict.
163
+ const current = await currentAtPath(client, repo, path);
164
+ if (!current) {
165
+ return createAtPath(deps, path, user, intr.computed_hash);
166
+ }
167
+ return occupiedPath(deps, path, intr.computed_hash, current);
168
+ }
169
+ /**
170
+ * `stale_prev` on a put: dest occupied → rebase local bytes onto the remote
171
+ * current (Obsidian keeps typing after we already pushed a snapshot); dest
172
+ * empty and the document currently lives in the system namespace (a premature
173
+ * local delete of a rename source) → put from that current version to restore
174
+ * identity; dest empty with no recoverable current → create so the local file
175
+ * is not stranded.
176
+ */
177
+ async function recoverStalePut(deps, path, user, computedHash, err) {
178
+ const { client, store, repo } = deps;
179
+ const log = deps.log ?? (() => { });
180
+ const destCurrent = await currentAtPath(client, repo, path);
181
+ if (destCurrent) {
182
+ if (computedHash === destCurrent.content_hash) {
183
+ return occupiedPath(deps, path, computedHash, destCurrent);
184
+ }
185
+ try {
186
+ // Same-path continuation: the editor is still on an older $version but
187
+ // has newer bytes. Put those bytes on top of current instead of parking
188
+ // a sibling that then gets fed back over the open note.
189
+ return await commitPut(deps, path, destCurrent.version_id, user, "update");
190
+ }
191
+ catch (rebaseErr) {
192
+ if (!(rebaseErr instanceof KernelError &&
193
+ (rebaseErr.code === "stale_prev" || rebaseErr.code === "path_taken"))) {
194
+ throw rebaseErr;
195
+ }
196
+ await store.write(withVersionSuffix(path, destCurrent.version_id), renderIgnoredSibling(destCurrent));
197
+ log(`conflict\t${path}`);
198
+ return "conflict";
199
+ }
200
+ }
201
+ const data = err.data;
202
+ const currentId = data.current_version_id ?? null;
203
+ let currentPath = data.current_path ?? null;
204
+ if (currentId && !currentPath) {
205
+ try {
206
+ currentPath = (await client.docs.get_version(repo, currentId)).path;
207
+ }
208
+ catch {
209
+ // Leave currentPath unset; restore is skipped and we may create.
210
+ }
211
+ }
212
+ if (currentId && currentPath && isDeletedPath(currentPath)) {
213
+ try {
214
+ return await commitPut(deps, path, currentId, user, "restore");
215
+ }
216
+ catch (restoreErr) {
217
+ if (!(restoreErr instanceof KernelError && restoreErr.code === "stale_prev"))
218
+ throw restoreErr;
219
+ // Raced; fall through to create so the file is not left unpushable.
220
+ }
221
+ }
222
+ if (currentId && currentPath && !isDeletedPath(currentPath)) {
223
+ // Still live at another path with a newer version — local rename vs
224
+ // remote edit. Park the remote current; keep local bytes.
225
+ const current = await client.docs.get_version(repo, currentId);
226
+ await store.write(withVersionSuffix(path, current.version_id), renderIgnoredSibling(current));
227
+ log(`conflict\t${path}`);
228
+ return "conflict";
229
+ }
230
+ // Dest is empty and we could not follow the document — create so a renamed
231
+ // (or late-arriving) file is never stranded.
232
+ return createAtPath(deps, path, user, computedHash);
233
+ }
234
+ async function createAtPath(deps, path, user, computedHash) {
235
+ const { client, store, repo, map } = deps;
236
+ const log = deps.log ?? (() => { });
237
+ try {
238
+ const v = await client.docs.create(repo, path, user);
239
+ await ackLocalWrite(store, path, v, user);
240
+ map.set(path, { version_id: v.version_id, content_hash: v.content_hash });
241
+ log(`create\t${path}`);
242
+ return "created";
243
+ }
244
+ catch (err) {
245
+ if (err instanceof KernelError && err.code === "create_conflict") {
246
+ const raced = await currentAtPath(client, repo, path);
247
+ if (raced)
248
+ return occupiedPath(deps, path, computedHash, raced);
249
+ }
250
+ throw err;
251
+ }
252
+ }
253
+ async function commitPut(deps, path, prevVersionId, user, logLabel) {
254
+ const { client, store, repo, map } = deps;
255
+ const log = deps.log ?? (() => { });
256
+ const v = await client.docs.put(repo, prevVersionId, path, user);
257
+ await ackLocalWrite(store, path, v, user);
258
+ dropMapEntriesForVersion(map, prevVersionId, path);
259
+ map.set(path, { version_id: v.version_id, content_hash: v.content_hash });
260
+ log(`${logLabel}\t${path}`);
261
+ return "updated";
262
+ }
263
+ /**
264
+ * After a successful kernel write, restamp the local file. If the editor saved
265
+ * again during the round-trip, keep those bytes and only update provenance —
266
+ * never write the snapshot we just pushed over newer typing.
267
+ */
268
+ async function ackLocalWrite(store, path, v, pushed) {
269
+ const now = await store.read(path);
270
+ if (now === null)
271
+ return;
272
+ const current = stripToUserContent(now);
273
+ if (current.body === pushed.body && current.frontmatter_raw === pushed.frontmatter_raw) {
274
+ await store.write(path, renderMaterialized(v), { preserveMtime: true });
275
+ return;
276
+ }
277
+ await store.write(path, stampProvenance(now, v.version_id, v.content_hash), {
278
+ preserveMtime: true,
279
+ });
280
+ }
281
+ /** The occupied-path rule (§4.4): hash match → adopt; differ → conflict park. */
282
+ async function occupiedPath(deps, path, computedHash, current) {
283
+ const { store, map } = deps;
284
+ const log = deps.log ?? (() => { });
285
+ if (computedHash === current.content_hash) {
286
+ // Clean local copy missing metadata → inject remote provenance in place, no
287
+ // server write. Re-render from the current version so intrinsics are exact.
288
+ await store.write(path, renderMaterialized(current), { preserveMtime: true });
289
+ map.set(path, { version_id: current.version_id, content_hash: current.content_hash });
290
+ return "clean";
291
+ }
292
+ await store.write(withVersionSuffix(path, current.version_id), renderIgnoredSibling(current));
293
+ log(`conflict\t${path}`);
294
+ return "conflict";
295
+ }
296
+ async function currentAtPath(client, repo, path) {
297
+ try {
298
+ return await client.docs.get(repo, path);
299
+ }
300
+ catch (err) {
301
+ if (err instanceof KernelError && err.code === "doc_not_found")
302
+ return null;
303
+ throw err;
304
+ }
305
+ }
306
+ function findMapPathByVersion(map, versionId) {
307
+ for (const [path, entry] of map) {
308
+ if (entry.version_id === versionId)
309
+ return path;
310
+ }
311
+ return undefined;
312
+ }
313
+ /** After a move/restore, the source path must not still look live for unlink. */
314
+ function dropMapEntriesForVersion(map, versionId, keepPath) {
315
+ for (const [path, entry] of map) {
316
+ if (path !== keepPath && entry.version_id === versionId)
317
+ map.delete(path);
318
+ }
319
+ }
320
+ function isDeletedPath(path) {
321
+ return pathIsInSystemNamespace(path, HARDCODED_DEFAULTS.system_sigils);
322
+ }
323
+ function stripToUserContent(text) {
324
+ const lf = text.replace(/\r\n/g, "\n");
325
+ const { frontmatter_raw, body } = split(lf);
326
+ return { frontmatter_raw: extractSystemProperties(frontmatter_raw).raw, body };
327
+ }
328
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/sync/push.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Startup reconciliation (sync/history plan §4.9) — the entirety of
3
+ * `mrplex sync --once`. No watcher, no remote deletes (by construction, §4.6).
4
+ *
5
+ * The pass walks the remote live set (history.index through a safe head R) and
6
+ * the local files, then resolves each path/identity against the §4.9 table.
7
+ * Every verdict is deterministic and non-destructive: local edits are pushed,
8
+ * clean local copies adopt remote provenance in place, and true conflicts park
9
+ * the remote current as an ignored sibling rather than clobbering local bytes.
10
+ *
11
+ * This module is transport-agnostic: it drives a `KernelClient` and a small
12
+ * `FileStore` seam (real fs in `fs-store.ts`, in-memory in tests), so the whole
13
+ * §4.9 table is testable against a local kernel without touching disk.
14
+ */
15
+ import type { KernelClient } from "../client/kernel-client.js";
16
+ import type { ScopeFilter } from "./paths.js";
17
+ /** The filesystem seam the reconciler drives (real fs or in-memory test double). */
18
+ export type FileStore = {
19
+ /** All in-scope doc paths present locally (repo-relative POSIX). */
20
+ list(): Promise<string[]>;
21
+ /** File text at a doc path, or null if absent. */
22
+ read(docPath: string): Promise<string | null>;
23
+ /**
24
+ * Write file text at a doc path (creating parent dirs).
25
+ * `preserveMtime` keeps the previous mtime — provenance stamps must not look
26
+ * newer than an Obsidian client's still-unsynced buffer or iCloud/Obsidian
27
+ * Sync will push the stamped snapshot back and clobber in-progress typing.
28
+ */
29
+ write(docPath: string, text: string, opts?: {
30
+ preserveMtime?: boolean;
31
+ }): Promise<void>;
32
+ /** Remove the file at a doc path (no-op if already absent). */
33
+ remove(docPath: string): Promise<void>;
34
+ };
35
+ /** One reconciliation action, for reporting + dry-run (§4.1 `--dry-run`). */
36
+ export type SyncAction = {
37
+ path: string;
38
+ verdict: "clean" | "adopt" | "materialize" | "push" | "conflict" | "delete-local" | "resurrect" | "ignored" | "skip";
39
+ detail?: string;
40
+ };
41
+ export type ReconcileOptions = {
42
+ repo: string;
43
+ dryRun?: boolean;
44
+ log?: (msg: string) => void;
45
+ };
46
+ export type ReconcileReport = {
47
+ through_version: string;
48
+ actions: SyncAction[];
49
+ };
50
+ /**
51
+ * Run the full §4.9 reconciliation once. Returns the safe head R (the cursor to
52
+ * persist) and the list of actions taken (or planned, under dryRun).
53
+ */
54
+ export declare function reconcileOnce(client: KernelClient, store: FileStore, scope: ScopeFilter, opts: ReconcileOptions): Promise<ReconcileReport>;