@lapis-notes/api 0.1.0

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 (400) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE.md +4 -0
  3. package/README.md +43 -0
  4. package/dist/__tests__/data-adapter-conformance.js +205 -0
  5. package/dist/__tests__/lapis-runtime-manifest.js +32 -0
  6. package/dist/__tests__/svelte-internal-client.d.ts +3 -0
  7. package/dist/agent-result-views.d.ts +52 -0
  8. package/dist/agent-result-views.js +89 -0
  9. package/dist/agent-skills.d.ts +99 -0
  10. package/dist/agent-skills.js +182 -0
  11. package/dist/agent-tools.d.ts +92 -0
  12. package/dist/agent-tools.js +139 -0
  13. package/dist/app-database-host.d.ts +4 -0
  14. package/dist/app-database-host.js +4 -0
  15. package/dist/app-shell-plugin-persistence.d.ts +15 -0
  16. package/dist/app-shell-plugin-persistence.js +34 -0
  17. package/dist/app-url.d.ts +54 -0
  18. package/dist/app-url.js +269 -0
  19. package/dist/application-compatibility.d.ts +31 -0
  20. package/dist/application-compatibility.js +64 -0
  21. package/dist/application-state.svelte.d.ts +14 -0
  22. package/dist/application-state.svelte.js +30 -0
  23. package/dist/assets/lapis-logo.svg +471 -0
  24. package/dist/bases.d.ts +281 -0
  25. package/dist/bases.js +475 -0
  26. package/dist/cache.svelte.d.ts +569 -0
  27. package/dist/cache.svelte.js +1433 -0
  28. package/dist/command.svelte.d.ts +337 -0
  29. package/dist/command.svelte.js +571 -0
  30. package/dist/compat.d.ts +29 -0
  31. package/dist/compat.js +30 -0
  32. package/dist/components/configuration/configuration.css +199 -0
  33. package/dist/components/configuration/date-setting.svelte +71 -0
  34. package/dist/components/configuration/date-setting.svelte.d.ts +8 -0
  35. package/dist/components/configuration/list-setting.svelte +128 -0
  36. package/dist/components/configuration/list-setting.svelte.d.ts +13 -0
  37. package/dist/components/configuration/object-array-row-grip.svelte +78 -0
  38. package/dist/components/configuration/object-array-row-grip.svelte.d.ts +9 -0
  39. package/dist/components/configuration/object-array-row.svelte +163 -0
  40. package/dist/components/configuration/object-array-row.svelte.d.ts +19 -0
  41. package/dist/components/configuration/object-array-setting.svelte +196 -0
  42. package/dist/components/configuration/object-array-setting.svelte.d.ts +15 -0
  43. package/dist/components/configuration/object-array-table-dnd.d.ts +8 -0
  44. package/dist/components/configuration/object-array-table-dnd.js +10 -0
  45. package/dist/components/configuration/object-array-utils.d.ts +11 -0
  46. package/dist/components/configuration/object-array-utils.js +103 -0
  47. package/dist/components/configuration/object-grid-setting.svelte +79 -0
  48. package/dist/components/configuration/object-grid-setting.svelte.d.ts +10 -0
  49. package/dist/components/configuration/object-map-row.svelte +147 -0
  50. package/dist/components/configuration/object-map-row.svelte.d.ts +15 -0
  51. package/dist/components/configuration/object-map-setting.svelte +137 -0
  52. package/dist/components/configuration/object-map-setting.svelte.d.ts +14 -0
  53. package/dist/components/configuration/object-map-types.d.ts +5 -0
  54. package/dist/components/configuration/object-map-types.js +1 -0
  55. package/dist/components/configuration/options-combobox-setting.svelte +129 -0
  56. package/dist/components/configuration/options-combobox-setting.svelte.d.ts +12 -0
  57. package/dist/components/copy-button/button.svelte +93 -0
  58. package/dist/components/copy-button/button.svelte.d.ts +13 -0
  59. package/dist/components/copy-button/index.d.ts +2 -0
  60. package/dist/components/copy-button/index.js +2 -0
  61. package/dist/components/editor/editor.css +565 -0
  62. package/dist/components/editor/editor.d.ts +33 -0
  63. package/dist/components/editor/editor.js +132 -0
  64. package/dist/components/editor/editor.svelte +176 -0
  65. package/dist/components/editor/editor.svelte.d.ts +15 -0
  66. package/dist/components/editor/embedded-editor-surface.d.ts +12 -0
  67. package/dist/components/editor/embedded-editor-surface.js +22 -0
  68. package/dist/components/editor/embedded-editor-surface.svelte +115 -0
  69. package/dist/components/editor/embedded-editor-surface.svelte.d.ts +17 -0
  70. package/dist/components/editor/extensions/autocomplete/index.d.ts +2 -0
  71. package/dist/components/editor/extensions/autocomplete/index.js +2 -0
  72. package/dist/components/editor/extensions/autocomplete/lapis-code-mirror-autocomplete.d.ts +16 -0
  73. package/dist/components/editor/extensions/autocomplete/lapis-code-mirror-autocomplete.js +34 -0
  74. package/dist/components/editor/extensions/autocomplete/vault-completion-sources.d.ts +27 -0
  75. package/dist/components/editor/extensions/autocomplete/vault-completion-sources.js +55 -0
  76. package/dist/components/editor/extensions/class-highlighter/index.d.ts +1 -0
  77. package/dist/components/editor/extensions/class-highlighter/index.js +47 -0
  78. package/dist/components/editor/extensions/fold-gutter/index.d.ts +8 -0
  79. package/dist/components/editor/extensions/fold-gutter/index.js +40 -0
  80. package/dist/components/editor/extensions/gutter-line-style-sync/index.d.ts +2 -0
  81. package/dist/components/editor/extensions/gutter-line-style-sync/index.js +118 -0
  82. package/dist/components/editor/extensions/lint/index.d.ts +4 -0
  83. package/dist/components/editor/extensions/lint/index.js +4 -0
  84. package/dist/components/editor/extensions/lint/lapis-code-mirror-lint.d.ts +26 -0
  85. package/dist/components/editor/extensions/lint/lapis-code-mirror-lint.js +101 -0
  86. package/dist/components/editor/extensions/lint/lapis-lint-diagnostic-helpers.d.ts +8 -0
  87. package/dist/components/editor/extensions/lint/lapis-lint-diagnostic-helpers.js +7 -0
  88. package/dist/components/editor/extensions/lint/lapis-lint-diagnostic.d.ts +20 -0
  89. package/dist/components/editor/extensions/lint/lapis-lint-diagnostic.js +47 -0
  90. package/dist/components/editor/extensions/lint/lapis-lint-hover-tooltip.d.ts +9 -0
  91. package/dist/components/editor/extensions/lint/lapis-lint-hover-tooltip.js +373 -0
  92. package/dist/components/editor/extensions/lint/lapis-lint-inline-widget.d.ts +31 -0
  93. package/dist/components/editor/extensions/lint/lapis-lint-inline-widget.js +191 -0
  94. package/dist/components/editor/extensions/lint/lapis-lint-tooltip.svelte +176 -0
  95. package/dist/components/editor/extensions/lint/lapis-lint-tooltip.svelte.d.ts +15 -0
  96. package/dist/components/editor/extensions/lint/lint-tooltip-actions.d.ts +11 -0
  97. package/dist/components/editor/extensions/lint/lint-tooltip-actions.js +21 -0
  98. package/dist/components/editor/extensions/lint/mount-lint-tooltip.d.ts +16 -0
  99. package/dist/components/editor/extensions/lint/mount-lint-tooltip.js +60 -0
  100. package/dist/components/editor/extensions/search/dom.d.ts +2 -0
  101. package/dist/components/editor/extensions/search/dom.js +6 -0
  102. package/dist/components/editor/extensions/search/index.d.ts +35 -0
  103. package/dist/components/editor/extensions/search/index.js +44 -0
  104. package/dist/components/editor/extensions/search/search.svelte +335 -0
  105. package/dist/components/editor/extensions/search/search.svelte.d.ts +8 -0
  106. package/dist/components/editor/index.d.ts +9 -0
  107. package/dist/components/editor/index.js +9 -0
  108. package/dist/components/editor/language-service/dom.d.ts +1 -0
  109. package/dist/components/editor/language-service/dom.js +6 -0
  110. package/dist/components/editor/language-service/index.d.ts +28 -0
  111. package/dist/components/editor/language-service/index.js +342 -0
  112. package/dist/components/empty-view/empty-view.css +39 -0
  113. package/dist/components/empty-view/empty-view.svelte +26 -0
  114. package/dist/components/empty-view/empty-view.svelte.d.ts +9 -0
  115. package/dist/components/empty-view/index.d.ts +2 -0
  116. package/dist/components/empty-view/index.js +2 -0
  117. package/dist/components/icon/icon-list.css +74 -0
  118. package/dist/components/icon/icon-list.svelte +213 -0
  119. package/dist/components/icon/icon-list.svelte.d.ts +11 -0
  120. package/dist/components/icon/icon.svelte +15 -0
  121. package/dist/components/icon/icon.svelte.d.ts +7 -0
  122. package/dist/components/icon/index.d.ts +3 -0
  123. package/dist/components/icon/index.js +3 -0
  124. package/dist/components/menu/context-menu.svelte +89 -0
  125. package/dist/components/menu/context-menu.svelte.d.ts +11 -0
  126. package/dist/components/menu/drawer-menu.svelte +83 -0
  127. package/dist/components/menu/drawer-menu.svelte.d.ts +8 -0
  128. package/dist/components/menu/dropdown-menu.svelte +89 -0
  129. package/dist/components/menu/dropdown-menu.svelte.d.ts +11 -0
  130. package/dist/components/menu/index.d.ts +6 -0
  131. package/dist/components/menu/index.js +6 -0
  132. package/dist/components/menu/menu.css +139 -0
  133. package/dist/components/menu/menu.svelte +37 -0
  134. package/dist/components/menu/menu.svelte.d.ts +11 -0
  135. package/dist/components/menu/mobile-menu-query.svelte.d.ts +3 -0
  136. package/dist/components/menu/mobile-menu-query.svelte.js +21 -0
  137. package/dist/components/menu/popover-menu.svelte +90 -0
  138. package/dist/components/menu/popover-menu.svelte.d.ts +10 -0
  139. package/dist/components/select/index.d.ts +2 -0
  140. package/dist/components/select/index.js +2 -0
  141. package/dist/components/select/select.svelte +72 -0
  142. package/dist/components/select/select.svelte.d.ts +25 -0
  143. package/dist/configuration-option-source-providers.d.ts +17 -0
  144. package/dist/configuration-option-source-providers.js +59 -0
  145. package/dist/configuration-option-source-registry.d.ts +59 -0
  146. package/dist/configuration-option-source-registry.js +123 -0
  147. package/dist/configuration.svelte.d.ts +258 -0
  148. package/dist/configuration.svelte.js +887 -0
  149. package/dist/context-keys.svelte.d.ts +42 -0
  150. package/dist/context-keys.svelte.js +278 -0
  151. package/dist/context.svelte.d.ts +247 -0
  152. package/dist/context.svelte.js +540 -0
  153. package/dist/daily-document-provider.d.ts +38 -0
  154. package/dist/daily-document-provider.js +176 -0
  155. package/dist/design-core-button-types.d.ts +7 -0
  156. package/dist/design-core-button-types.js +1 -0
  157. package/dist/diagnostics.d.ts +93 -0
  158. package/dist/diagnostics.js +84 -0
  159. package/dist/dialog-portal.d.ts +3 -0
  160. package/dist/dialog-portal.js +3 -0
  161. package/dist/docs-api.d.ts +44 -0
  162. package/dist/docs-api.js +29 -0
  163. package/dist/editor-view-registry.d.ts +35 -0
  164. package/dist/editor-view-registry.js +112 -0
  165. package/dist/editor.svelte.d.ts +126 -0
  166. package/dist/editor.svelte.js +454 -0
  167. package/dist/embed-registry.d.ts +15 -0
  168. package/dist/embed-registry.js +16 -0
  169. package/dist/enhance.d.ts +111 -0
  170. package/dist/enhance.js +309 -0
  171. package/dist/events.d.ts +28 -0
  172. package/dist/events.js +49 -0
  173. package/dist/file-manager.d.ts +90 -0
  174. package/dist/file-manager.js +289 -0
  175. package/dist/file-tools/app-tools.d.ts +3 -0
  176. package/dist/file-tools/app-tools.js +242 -0
  177. package/dist/file-tools/index.d.ts +3 -0
  178. package/dist/file-tools/index.js +3 -0
  179. package/dist/file-tools/paths.d.ts +14 -0
  180. package/dist/file-tools/paths.js +33 -0
  181. package/dist/file-tools/vault-operations.d.ts +3 -0
  182. package/dist/file-tools/vault-operations.js +60 -0
  183. package/dist/generated/plugin-host-modules.schema.generated.d.ts +371 -0
  184. package/dist/generated/plugin-host-modules.schema.generated.js +374 -0
  185. package/dist/glob.d.ts +15 -0
  186. package/dist/glob.js +68 -0
  187. package/dist/history.svelte.d.ts +24 -0
  188. package/dist/history.svelte.js +74 -0
  189. package/dist/hooks/createVirtualizer.svelte.d.ts +7 -0
  190. package/dist/hooks/createVirtualizer.svelte.js +70 -0
  191. package/dist/hooks/index.d.ts +5 -0
  192. package/dist/hooks/index.js +5 -0
  193. package/dist/hooks/is-mobile.svelte.d.ts +5 -0
  194. package/dist/hooks/is-mobile.svelte.js +23 -0
  195. package/dist/hooks/mountComponent.svelte.d.ts +7 -0
  196. package/dist/hooks/mountComponent.svelte.js +16 -0
  197. package/dist/hooks/use-text-highlight.d.ts +17 -0
  198. package/dist/hooks/use-text-highlight.js +82 -0
  199. package/dist/hooks/useBoundingRect.svelte.d.ts +11 -0
  200. package/dist/hooks/useBoundingRect.svelte.js +46 -0
  201. package/dist/icons/index.d.ts +57 -0
  202. package/dist/icons/index.js +288 -0
  203. package/dist/index.d.ts +87 -0
  204. package/dist/index.js +87 -0
  205. package/dist/language-service/index.d.ts +9 -0
  206. package/dist/language-service/index.js +7 -0
  207. package/dist/language-service/manager.d.ts +47 -0
  208. package/dist/language-service/manager.js +404 -0
  209. package/dist/language-service/types.d.ts +92 -0
  210. package/dist/language-service/types.js +1 -0
  211. package/dist/language-service/virtual-document.d.ts +11 -0
  212. package/dist/language-service/virtual-document.js +45 -0
  213. package/dist/language-service/worker-protocol.d.ts +63 -0
  214. package/dist/language-service/worker-protocol.js +1 -0
  215. package/dist/language-service/worker-provider.d.ts +9 -0
  216. package/dist/language-service/worker-provider.js +38 -0
  217. package/dist/lapis-extension.d.ts +244 -0
  218. package/dist/lapis-extension.js +591 -0
  219. package/dist/links.d.ts +34 -0
  220. package/dist/links.js +191 -0
  221. package/dist/loaders.d.ts +9 -0
  222. package/dist/loaders.js +59 -0
  223. package/dist/localization-manager.svelte.d.ts +56 -0
  224. package/dist/localization-manager.svelte.js +157 -0
  225. package/dist/logging.d.ts +172 -0
  226. package/dist/logging.js +352 -0
  227. package/dist/markdown-extension-registry.d.ts +31 -0
  228. package/dist/markdown-extension-registry.js +31 -0
  229. package/dist/markdown-file-surface-registry.d.ts +25 -0
  230. package/dist/markdown-file-surface-registry.js +23 -0
  231. package/dist/markdown.d.ts +205 -0
  232. package/dist/markdown.js +147 -0
  233. package/dist/menu-utils.d.ts +1 -0
  234. package/dist/menu-utils.js +47 -0
  235. package/dist/menu.svelte.d.ts +121 -0
  236. package/dist/menu.svelte.js +295 -0
  237. package/dist/metadata-value.d.ts +10 -0
  238. package/dist/metadata-value.js +173 -0
  239. package/dist/metadata.svelte.d.ts +106 -0
  240. package/dist/metadata.svelte.js +608 -0
  241. package/dist/notifications.d.ts +209 -0
  242. package/dist/notifications.js +338 -0
  243. package/dist/open-file.d.ts +23 -0
  244. package/dist/open-file.js +102 -0
  245. package/dist/platform.d.ts +86 -0
  246. package/dist/platform.js +86 -0
  247. package/dist/plugin-asset-server.d.ts +60 -0
  248. package/dist/plugin-asset-server.js +222 -0
  249. package/dist/plugin-assets.d.ts +4 -0
  250. package/dist/plugin-assets.js +3 -0
  251. package/dist/plugin-capability-facade.d.ts +104 -0
  252. package/dist/plugin-capability-facade.js +61 -0
  253. package/dist/plugin-dependency-resolver.d.ts +36 -0
  254. package/dist/plugin-dependency-resolver.js +54 -0
  255. package/dist/plugin-dependency-scanner.d.ts +9 -0
  256. package/dist/plugin-dependency-scanner.js +274 -0
  257. package/dist/plugin-distribution/canonical-json.d.ts +1 -0
  258. package/dist/plugin-distribution/canonical-json.js +40 -0
  259. package/dist/plugin-distribution/compatibility.d.ts +5 -0
  260. package/dist/plugin-distribution/compatibility.js +38 -0
  261. package/dist/plugin-distribution/errors.d.ts +11 -0
  262. package/dist/plugin-distribution/errors.js +13 -0
  263. package/dist/plugin-distribution/hashes.d.ts +2 -0
  264. package/dist/plugin-distribution/hashes.js +17 -0
  265. package/dist/plugin-distribution/index.d.ts +18 -0
  266. package/dist/plugin-distribution/index.js +18 -0
  267. package/dist/plugin-distribution/installed-plugin-state.d.ts +20 -0
  268. package/dist/plugin-distribution/installed-plugin-state.js +73 -0
  269. package/dist/plugin-distribution/installer.d.ts +89 -0
  270. package/dist/plugin-distribution/installer.js +395 -0
  271. package/dist/plugin-distribution/manager.d.ts +128 -0
  272. package/dist/plugin-distribution/manager.js +358 -0
  273. package/dist/plugin-distribution/path-safety.d.ts +2 -0
  274. package/dist/plugin-distribution/path-safety.js +20 -0
  275. package/dist/plugin-distribution/plugin-bundle-verifier.d.ts +28 -0
  276. package/dist/plugin-distribution/plugin-bundle-verifier.js +96 -0
  277. package/dist/plugin-distribution/plugin-bundle-worker.d.ts +34 -0
  278. package/dist/plugin-distribution/plugin-bundle-worker.js +99 -0
  279. package/dist/plugin-distribution/plugin-bundle.d.ts +16 -0
  280. package/dist/plugin-distribution/plugin-bundle.js +244 -0
  281. package/dist/plugin-distribution/plugin-bundle.worker.d.ts +1 -0
  282. package/dist/plugin-distribution/plugin-bundle.worker.js +38 -0
  283. package/dist/plugin-distribution/registry-cache.d.ts +26 -0
  284. package/dist/plugin-distribution/registry-cache.js +85 -0
  285. package/dist/plugin-distribution/registry-client.d.ts +36 -0
  286. package/dist/plugin-distribution/registry-client.js +115 -0
  287. package/dist/plugin-distribution/reserved-ids.d.ts +4 -0
  288. package/dist/plugin-distribution/reserved-ids.js +10 -0
  289. package/dist/plugin-distribution/runtime-validation.d.ts +14 -0
  290. package/dist/plugin-distribution/runtime-validation.js +388 -0
  291. package/dist/plugin-distribution/schemas.d.ts +4895 -0
  292. package/dist/plugin-distribution/schemas.js +299 -0
  293. package/dist/plugin-distribution/signing.d.ts +5 -0
  294. package/dist/plugin-distribution/signing.js +38 -0
  295. package/dist/plugin-distribution/types.d.ts +217 -0
  296. package/dist/plugin-distribution/types.js +1 -0
  297. package/dist/plugin-install-progress.d.ts +15 -0
  298. package/dist/plugin-install-progress.js +86 -0
  299. package/dist/plugin-manager.d.ts +306 -0
  300. package/dist/plugin-manager.js +2848 -0
  301. package/dist/plugin-runtime-entry.d.ts +19 -0
  302. package/dist/plugin-runtime-entry.js +135 -0
  303. package/dist/plugin.d.ts +350 -0
  304. package/dist/plugin.js +754 -0
  305. package/dist/popover.d.ts +20 -0
  306. package/dist/popover.js +32 -0
  307. package/dist/promise-worker.d.ts +22 -0
  308. package/dist/promise-worker.js +65 -0
  309. package/dist/prompt-confirm.d.ts +9 -0
  310. package/dist/prompt-confirm.js +33 -0
  311. package/dist/search-document-provider.d.ts +45 -0
  312. package/dist/search-document-provider.js +54 -0
  313. package/dist/search-query/index.d.ts +78 -0
  314. package/dist/search-query/index.js +426 -0
  315. package/dist/search-query/parser.d.ts +3 -0
  316. package/dist/search-query/parser.js +16 -0
  317. package/dist/search-query/parser.terms.js +26 -0
  318. package/dist/search-query/search-query.grammar +128 -0
  319. package/dist/search.d.ts +19 -0
  320. package/dist/search.js +104 -0
  321. package/dist/setting-section-layout.d.ts +19 -0
  322. package/dist/setting-section-layout.js +194 -0
  323. package/dist/settings.svelte.d.ts +474 -0
  324. package/dist/settings.svelte.js +1421 -0
  325. package/dist/source-text-file-view.d.ts +25 -0
  326. package/dist/source-text-file-view.js +78 -0
  327. package/dist/status-bar.svelte.d.ts +29 -0
  328. package/dist/status-bar.svelte.js +72 -0
  329. package/dist/storage/app-database.d.ts +860 -0
  330. package/dist/storage/app-database.js +2372 -0
  331. package/dist/storage/blob-store.d.ts +12 -0
  332. package/dist/storage/blob-store.js +1 -0
  333. package/dist/storage/browser-app-database-coordination.d.ts +35 -0
  334. package/dist/storage/browser-app-database-coordination.js +207 -0
  335. package/dist/storage/browser-coordinated-app-database.d.ts +109 -0
  336. package/dist/storage/browser-coordinated-app-database.js +704 -0
  337. package/dist/storage/browser.d.ts +153 -0
  338. package/dist/storage/browser.js +794 -0
  339. package/dist/storage/desktop-native.d.ts +184 -0
  340. package/dist/storage/desktop-native.js +415 -0
  341. package/dist/storage/fs.d.ts +188 -0
  342. package/dist/storage/fs.js +307 -0
  343. package/dist/storage/index-projection.d.ts +236 -0
  344. package/dist/storage/index-projection.js +402 -0
  345. package/dist/storage/index.d.ts +17 -0
  346. package/dist/storage/index.js +17 -0
  347. package/dist/storage/keyval.d.ts +94 -0
  348. package/dist/storage/keyval.js +199 -0
  349. package/dist/storage/memory-vault-adapter.d.ts +44 -0
  350. package/dist/storage/memory-vault-adapter.js +351 -0
  351. package/dist/storage/native-vault-bootstrap-kv.d.ts +22 -0
  352. package/dist/storage/native-vault-bootstrap-kv.js +96 -0
  353. package/dist/storage/path.d.ts +22 -0
  354. package/dist/storage/path.js +110 -0
  355. package/dist/storage/search-embedding-provider.d.ts +75 -0
  356. package/dist/storage/search-embedding-provider.js +446 -0
  357. package/dist/storage/search-embedding-worker.d.ts +1 -0
  358. package/dist/storage/search-embedding-worker.js +63 -0
  359. package/dist/storage/search-query-evaluator.d.ts +14 -0
  360. package/dist/storage/search-query-evaluator.js +678 -0
  361. package/dist/storage/task-projection.d.ts +152 -0
  362. package/dist/storage/task-projection.js +154 -0
  363. package/dist/storage/turso-app-database.d.ts +150 -0
  364. package/dist/storage/turso-app-database.js +2671 -0
  365. package/dist/storage/vault-session.d.ts +37 -0
  366. package/dist/storage/vault-session.js +196 -0
  367. package/dist/storage/vault-state.d.ts +74 -0
  368. package/dist/storage/vault-state.js +181 -0
  369. package/dist/storage/vault.svelte.d.ts +257 -0
  370. package/dist/storage/vault.svelte.js +661 -0
  371. package/dist/suggest.d.ts +193 -0
  372. package/dist/suggest.js +391 -0
  373. package/dist/tasks.d.ts +7 -0
  374. package/dist/tasks.js +18 -0
  375. package/dist/telemetry.d.ts +116 -0
  376. package/dist/telemetry.js +103 -0
  377. package/dist/utils.d.ts +55 -0
  378. package/dist/utils.js +314 -0
  379. package/dist/vault-api.d.ts +5 -0
  380. package/dist/vault-api.js +3 -0
  381. package/dist/vault-maintenance-commands.d.ts +5 -0
  382. package/dist/vault-maintenance-commands.js +32 -0
  383. package/dist/view.svelte.d.ts +215 -0
  384. package/dist/view.svelte.js +381 -0
  385. package/dist/vite-env.d.ts +2 -0
  386. package/dist/workspace-host-internal.d.ts +7 -0
  387. package/dist/workspace-host-internal.js +7 -0
  388. package/dist/workspace-host.d.ts +10 -0
  389. package/dist/workspace-host.js +14 -0
  390. package/dist/workspace-layout-commands.d.ts +3 -0
  391. package/dist/workspace-layout-commands.js +72 -0
  392. package/dist/workspace-layout-normalizer.d.ts +92 -0
  393. package/dist/workspace-layout-normalizer.js +301 -0
  394. package/dist/workspace-layouts.d.ts +7 -0
  395. package/dist/workspace-layouts.js +23 -0
  396. package/dist/workspace-trust.d.ts +32 -0
  397. package/dist/workspace-trust.js +72 -0
  398. package/dist/workspace.svelte.d.ts +796 -0
  399. package/dist/workspace.svelte.js +4380 -0
  400. package/package.json +240 -0
@@ -0,0 +1,1433 @@
1
+ import { md5, parseLinktext, stripHeadingForLink } from "./utils";
2
+ import { EventDispatcher } from "./events";
3
+ import { logging } from "./logging";
4
+ import { defaultLinkSettings, getLinkPath, normalizeVaultPath, } from "./links";
5
+ import { basename, TFile, } from "./storage";
6
+ import { debounce } from "lodash-es";
7
+ import { dirname, resolvePath } from "./storage/path";
8
+ export const METADATA_CACHE_BACKUP_PATH = ".lapis/cache/metadata-cache.json";
9
+ export const METADATA_CACHE_HOT_LIMIT = 512;
10
+ const METADATA_RECONCILE_CHECKPOINT_KEY = "metadata-cache.reconcile-checkpoint.v1";
11
+ const METADATA_RECONCILE_CHECKPOINT_VERSION = 1;
12
+ const FINGERPRINT_MASK = (1n << 128n) - 1n;
13
+ class StreamingManifestFingerprint {
14
+ count = 0;
15
+ sum = 0n;
16
+ xor = 0n;
17
+ add(value) {
18
+ const digest = BigInt(`0x${md5(value)}`);
19
+ this.count += 1;
20
+ this.sum = (this.sum + digest) & FINGERPRINT_MASK;
21
+ this.xor ^= digest;
22
+ }
23
+ finish(scope) {
24
+ return `${scope}:${this.count}:${this.sum.toString(16).padStart(32, "0")}:${this.xor.toString(16).padStart(32, "0")}`;
25
+ }
26
+ }
27
+ function isReconcileCheckpoint(value) {
28
+ return (isRecord(value) &&
29
+ value.version === METADATA_RECONCILE_CHECKPOINT_VERSION &&
30
+ typeof value.fingerprint === "string" &&
31
+ typeof value.completedAt === "number" &&
32
+ Number.isFinite(value.completedAt));
33
+ }
34
+ function yieldToUi() {
35
+ return new Promise((resolve) => {
36
+ setTimeout(resolve, 0);
37
+ });
38
+ }
39
+ function isCancellationError(error) {
40
+ return ((error instanceof Error && error.name === "AbortError") ||
41
+ (error instanceof Error && /cancel/iu.test(error.message)));
42
+ }
43
+ function notify(message) {
44
+ const Notice = globalThis.Notice;
45
+ if (typeof Notice === "function") {
46
+ new Notice(message);
47
+ }
48
+ else {
49
+ console.warn(message);
50
+ }
51
+ }
52
+ function isRecord(value) {
53
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
54
+ }
55
+ function cloneAppDatabaseChange(change) {
56
+ return {
57
+ revision: change.revision,
58
+ domains: [...change.domains],
59
+ paths: [...change.paths],
60
+ renamed: change.renamed?.map((entry) => ({ ...entry })),
61
+ reset: change.reset,
62
+ committedAt: change.committedAt,
63
+ };
64
+ }
65
+ export function getFrontMatterInfo(content) {
66
+ const regexp = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s;
67
+ let match;
68
+ if ((match = regexp.exec(content))) {
69
+ return {
70
+ exists: true,
71
+ frontmatter: match[1],
72
+ from: match.index,
73
+ to: regexp.lastIndex,
74
+ contentStart: match[0].length,
75
+ };
76
+ }
77
+ return { exists: false, frontmatter: "", from: 0, to: 0, contentStart: 0 };
78
+ }
79
+ export function parseFrontMatterTags(frontmatter) {
80
+ if (!frontmatter)
81
+ return null;
82
+ const tags = [];
83
+ const pushTag = (value) => {
84
+ const tag = value?.toString().trim();
85
+ if (!tag) {
86
+ return;
87
+ }
88
+ const normalized = tag.startsWith("#") ? tag : `#${tag}`;
89
+ if (/^#[/a-zA-Z0-9 _-]+$/.test(normalized)) {
90
+ tags.push(normalized);
91
+ }
92
+ };
93
+ if (typeof frontmatter === "object") {
94
+ for (const [key, value] of Object.entries(frontmatter)) {
95
+ const id = key.toLowerCase().trim();
96
+ if (id === "tags" || id === "tag") {
97
+ if (Array.isArray(value)) {
98
+ value.forEach(pushTag);
99
+ }
100
+ else if (typeof value === "string") {
101
+ value.split(/[,;\s]+/).forEach(pushTag);
102
+ }
103
+ }
104
+ }
105
+ }
106
+ return tags;
107
+ }
108
+ export function parseFrontMatterStringArray(frontmatter, key) {
109
+ if (!frontmatter || !(key in frontmatter))
110
+ return null;
111
+ const value = frontmatter[key];
112
+ if (Array.isArray(value)) {
113
+ return value.map((item) => item.toString());
114
+ }
115
+ if (typeof value === "string") {
116
+ return value
117
+ .split(",")
118
+ .map((item) => item.trim())
119
+ .filter(Boolean);
120
+ }
121
+ return null;
122
+ }
123
+ export function parseFrontMatterAliases(frontmatter) {
124
+ return (parseFrontMatterStringArray(frontmatter, "aliases") ??
125
+ parseFrontMatterStringArray(frontmatter, "alias"));
126
+ }
127
+ export function parseFrontMatterEntry(frontmatter, key) {
128
+ return frontmatter?.[key] ?? null;
129
+ }
130
+ export function iterateRefs(refs, cb) {
131
+ for (const ref of refs) {
132
+ if (cb(ref))
133
+ return true;
134
+ }
135
+ return false;
136
+ }
137
+ function uniquePaths(paths) {
138
+ return [...new Set([...paths].filter(Boolean))];
139
+ }
140
+ export function iterateCacheRefs(cache, cb) {
141
+ return iterateRefs([...(cache.links ?? []), ...(cache.embeds ?? [])], cb);
142
+ }
143
+ export function resolveSubpath(cache, subpath) {
144
+ const target = subpath.replace(/^#/, "").trim();
145
+ if (!target)
146
+ return null;
147
+ if (target.startsWith("^")) {
148
+ const id = target.slice(1);
149
+ const block = cache.blocks?.[id];
150
+ if (!block)
151
+ return null;
152
+ return {
153
+ type: "block",
154
+ block,
155
+ list: cache.listItems?.find((item) => item.id === id),
156
+ start: block.position.start,
157
+ end: block.position.end,
158
+ };
159
+ }
160
+ const footnoteId = /^\[(.+)]$/.exec(target)?.[1];
161
+ if (footnoteId) {
162
+ const footnote = cache.footnotes?.find((item) => item.id === footnoteId);
163
+ if (!footnote)
164
+ return null;
165
+ return {
166
+ type: "footnote",
167
+ footnote,
168
+ start: footnote.position.start,
169
+ end: footnote.position.end,
170
+ };
171
+ }
172
+ const normalized = stripHeadingForLink(target).toLowerCase();
173
+ const headings = cache.headings ?? [];
174
+ const index = headings.findIndex((heading) => {
175
+ return stripHeadingForLink(heading.heading).toLowerCase() === normalized;
176
+ });
177
+ if (index === -1)
178
+ return null;
179
+ const current = headings[index];
180
+ const next = headings[index + 1] ?? null;
181
+ return {
182
+ type: "heading",
183
+ current,
184
+ next,
185
+ start: current.position.start,
186
+ end: next?.position.start ?? null,
187
+ };
188
+ }
189
+ function clearObject(data) {
190
+ for (const key of Object.keys(data)) {
191
+ delete data[key];
192
+ }
193
+ }
194
+ function frontmatterValueType(value) {
195
+ if (Array.isArray(value))
196
+ return "array";
197
+ if (value === null)
198
+ return "null";
199
+ return typeof value;
200
+ }
201
+ function tagParts(tag) {
202
+ const parts = tag
203
+ .replace(/^#/, "")
204
+ .split("/")
205
+ .map((part) => part.trim())
206
+ .filter(Boolean);
207
+ const hierarchy = [];
208
+ for (let index = 1; index <= parts.length; index++) {
209
+ hierarchy.push(parts.slice(0, index).join("/"));
210
+ }
211
+ return { parts, hierarchy };
212
+ }
213
+ /**
214
+ * Indexes parsed markdown metadata, tags, headings, and resolved links for the
215
+ * active vault.
216
+ *
217
+ * @public
218
+ */
219
+ export class MetadataCache extends EventDispatcher {
220
+ app;
221
+ processors = new Map();
222
+ projectionContributors = new Set();
223
+ #fileCache = $state({});
224
+ #metadataCache = $state({});
225
+ #resolvedLinks = $state({});
226
+ #unresolvedLinks = $state({});
227
+ logger = logging.getLogger("cache");
228
+ disposed = false;
229
+ didLoad = false;
230
+ loadPromise = null;
231
+ disposePromise = null;
232
+ pendingOperations = new Set();
233
+ hotPathOrder = new Map();
234
+ localMutationPaths = new Set();
235
+ reconcileIndexChangeDepth = 0;
236
+ deferredReconcileIndexChange = null;
237
+ reconcileCheckpointReady = false;
238
+ reconcileCheckpointDirty = false;
239
+ reconcileCheckpointGeneration = 0;
240
+ activeReconcileMutations = 0;
241
+ reconcileCheckpointWrite = null;
242
+ snapshotLeaseCount = 0;
243
+ snapshotLeasePromise = null;
244
+ snapshotLeaseValue = null;
245
+ databaseChangeUnsubscribe = null;
246
+ handleVaultChange = (event, file) => this.handleChange(event, file);
247
+ handleVaultRename = (file, oldPath) => this.handleRename(file, oldPath);
248
+ saveSnapshotDebounced = debounce(() => void this.saveSnapshotNow(), 500);
249
+ constructor(app) {
250
+ super();
251
+ this.app = app;
252
+ app.vault.on("all", this.handleVaultChange);
253
+ app.vault.on("rename", this.handleVaultRename);
254
+ this.databaseChangeUnsubscribe =
255
+ app.appDatabase.subscribeToChanges?.((change) => this.handleDatabaseChange(change)) ?? null;
256
+ }
257
+ get metadataCache() {
258
+ return this.#metadataCache;
259
+ }
260
+ get resolvedLinks() {
261
+ return this.#resolvedLinks;
262
+ }
263
+ get unresolvedLinks() {
264
+ return this.#unresolvedLinks;
265
+ }
266
+ get fileCache() {
267
+ return this.#fileCache;
268
+ }
269
+ get initialized() {
270
+ return this.didLoad;
271
+ }
272
+ getAllItems() {
273
+ const map = new Map();
274
+ for (const [path, cache] of Object.entries(this.#fileCache)) {
275
+ const item = this.#metadataCache[cache.hash];
276
+ const file = this.app.vault.getFileByPath(path);
277
+ if (item && file) {
278
+ map.set(file, item);
279
+ }
280
+ }
281
+ return map;
282
+ }
283
+ scheduleSnapshotSave() {
284
+ if (this.disposed || this.snapshotLeaseCount === 0)
285
+ return;
286
+ this.saveSnapshotDebounced();
287
+ }
288
+ async flushSnapshotSave() {
289
+ this.saveSnapshotDebounced.cancel();
290
+ await this.saveSnapshotNow({ forceBackup: true });
291
+ }
292
+ async dispose(options = {}) {
293
+ if (this.disposePromise)
294
+ return this.disposePromise;
295
+ this.disposed = true;
296
+ this.app.vault.off("all", this.handleVaultChange);
297
+ this.app.vault.off("rename", this.handleVaultRename);
298
+ this.databaseChangeUnsubscribe?.();
299
+ this.databaseChangeUnsubscribe = null;
300
+ this.snapshotLeaseCount = 0;
301
+ this.snapshotLeasePromise = null;
302
+ this.snapshotLeaseValue = null;
303
+ this.saveSnapshotDebounced.cancel();
304
+ this.disposePromise = (async () => {
305
+ await Promise.allSettled([...this.pendingOperations]);
306
+ await this.persistReconcileCheckpoint({ allowDisposed: true });
307
+ void options;
308
+ })();
309
+ return this.disposePromise;
310
+ }
311
+ async saveSnapshotNow(_options = {}) {
312
+ const snapshot = await this.buildCompatibilitySnapshot();
313
+ try {
314
+ await this.app.appDatabase.saveMetadataSnapshot(snapshot);
315
+ }
316
+ catch (err) {
317
+ console.error("Failure saving metadata cache", err);
318
+ notify(`Failure saving metadata cache: ${err}`);
319
+ }
320
+ }
321
+ toJSON() {
322
+ return {
323
+ fileCache: $state.snapshot(this.#fileCache),
324
+ metadataCache: $state.snapshot(this.metadataCache),
325
+ resolvedLinks: $state.snapshot(this.resolvedLinks),
326
+ unresolvedLinks: $state.snapshot(this.unresolvedLinks),
327
+ };
328
+ }
329
+ applySnapshot(snapshot) {
330
+ if (this.disposed)
331
+ return;
332
+ this.didLoad = true;
333
+ this.#fileCache = snapshot.fileCache;
334
+ this.#metadataCache = snapshot.metadataCache;
335
+ this.#resolvedLinks = snapshot.resolvedLinks;
336
+ this.#unresolvedLinks = snapshot.unresolvedLinks;
337
+ }
338
+ async load() {
339
+ if (this.disposed)
340
+ return;
341
+ if (this.loadPromise)
342
+ return this.loadPromise;
343
+ this.loadPromise = this.performLoad();
344
+ this.trackOperation(this.loadPromise);
345
+ return this.loadPromise;
346
+ }
347
+ async performLoad() {
348
+ try {
349
+ await this.app.telemetry.measureAsync("metadata.cache.load", async (loadSpan) => this.app.notifications.withProgress({
350
+ title: "Loading metadata cache",
351
+ source: "Metadata",
352
+ location: "status",
353
+ persistOnError: true,
354
+ }, async (progress) => {
355
+ if (this.disposed)
356
+ return;
357
+ progress.report({ message: "Opening metadata store" });
358
+ await this.app.appDatabase.open();
359
+ if (this.disposed)
360
+ return;
361
+ this.didLoad = true;
362
+ this.trigger("loaded");
363
+ const total = this.countProcessableVaultFiles();
364
+ loadSpan.setAttribute("metadata.files.total", total);
365
+ const fingerprint = this.getReconciliationFingerprint();
366
+ const checkpoint = await this.app.appDatabase.getMeta(METADATA_RECONCILE_CHECKPOINT_KEY);
367
+ if (isReconcileCheckpoint(checkpoint) &&
368
+ checkpoint.fingerprint === fingerprint) {
369
+ this.reconcileCheckpointReady = true;
370
+ this.reconcileCheckpointDirty = false;
371
+ loadSpan.setAttribute("metadata.checkpoint", "hit");
372
+ progress.report({ message: "Metadata index ready" });
373
+ this.app.telemetry.recordEvent("metadata.reconcile.complete", {
374
+ checkpoint: "hit",
375
+ "files.total": total,
376
+ "files.processed": 0,
377
+ "files.changed": 0,
378
+ "files.deleted": 0,
379
+ });
380
+ return;
381
+ }
382
+ loadSpan.setAttribute("metadata.checkpoint", "miss");
383
+ progress.report({ message: "Reconciling metadata index" });
384
+ const summary = await this.reconcileDatabaseWithVault(progress);
385
+ if (this.disposed)
386
+ return;
387
+ this.reconcileCheckpointReady = true;
388
+ this.markReconcileCheckpointDirty();
389
+ await this.persistReconcileCheckpoint();
390
+ this.app.telemetry.recordEvent("metadata.reconcile.complete", {
391
+ checkpoint: "miss",
392
+ "files.total": summary.total,
393
+ "files.processed": summary.processed,
394
+ "files.changed": summary.changed,
395
+ "files.deleted": summary.deleted,
396
+ });
397
+ }));
398
+ }
399
+ catch (error) {
400
+ this.app.telemetry.recordEvent(isCancellationError(error)
401
+ ? "metadata.reconcile.cancelled"
402
+ : "metadata.reconcile.failed", { status: isCancellationError(error) ? "cancelled" : "failed" });
403
+ throw error;
404
+ }
405
+ }
406
+ async reconcileDatabaseWithVault(progress) {
407
+ const summary = {
408
+ total: this.countProcessableVaultFiles(),
409
+ processed: 0,
410
+ changed: 0,
411
+ deleted: 0,
412
+ batches: 0,
413
+ };
414
+ if (this.disposed)
415
+ return summary;
416
+ const reconcileSpan = this.app.telemetry.startSpan("metadata.reconcile", {
417
+ attributes: { "metadata.files.total": summary.total },
418
+ });
419
+ this.reconcileIndexChangeDepth += 1;
420
+ let processed = 0;
421
+ let total = summary.total;
422
+ let cursor;
423
+ try {
424
+ progress?.report({
425
+ current: 0,
426
+ total,
427
+ message: total > 0
428
+ ? `Reconciling metadata index (0 of ${total})`
429
+ : "Reconciling metadata index",
430
+ });
431
+ const report = async (path) => {
432
+ progress?.throwIfCancellationRequested();
433
+ processed += 1;
434
+ total = Math.max(total, processed);
435
+ progress?.report({
436
+ current: processed,
437
+ total,
438
+ message: `${path} (${processed} of ${total})`,
439
+ });
440
+ await yieldToUi();
441
+ };
442
+ const reportProcessing = async (path, stage = "Indexing") => {
443
+ progress?.throwIfCancellationRequested();
444
+ progress?.report({
445
+ current: processed,
446
+ total,
447
+ message: `${stage}: ${path} (${processed + 1} of ${total})`,
448
+ });
449
+ await yieldToUi();
450
+ };
451
+ do {
452
+ progress?.report({
453
+ current: processed,
454
+ total,
455
+ message: `Querying existing metadata index (${processed} of ${total})`,
456
+ });
457
+ const batchSpan = this.app.telemetry.startSpan("metadata.reconcile.batch", {
458
+ parent: reconcileSpan,
459
+ attributes: {
460
+ "metadata.batch.kind": "indexed",
461
+ "metadata.batch.limit": 500,
462
+ },
463
+ });
464
+ summary.batches += 1;
465
+ let page;
466
+ try {
467
+ page = await this.app.appDatabase.listIndexedFileManifest({
468
+ after: cursor,
469
+ limit: 500,
470
+ });
471
+ batchSpan.end({ "metadata.batch.rows": page.rows.length });
472
+ }
473
+ catch (error) {
474
+ batchSpan.recordException(error);
475
+ batchSpan.end();
476
+ throw error;
477
+ }
478
+ for (const indexed of page.rows) {
479
+ if (this.disposed)
480
+ return summary;
481
+ const current = this.app.vault.getFileByPath(indexed.path);
482
+ if (!current ||
483
+ !this.processors.has(current.extension.toLowerCase())) {
484
+ await this.mutateDatabasePaths([indexed.path], () => this.app.appDatabase.deleteIndexedFile(indexed.path));
485
+ this.evictHotPath(indexed.path);
486
+ summary.deleted += 1;
487
+ await report(indexed.path);
488
+ continue;
489
+ }
490
+ if (current.stat.mtime !== indexed.mtime ||
491
+ current.stat.size !== indexed.size ||
492
+ indexed.parserVersion !== this.parserSignature(current)) {
493
+ await reportProcessing(current.path);
494
+ await this.processFile(current, (stage) => reportProcessing(current.path, stage));
495
+ summary.changed += 1;
496
+ }
497
+ await report(current.path);
498
+ }
499
+ cursor = page.nextCursor;
500
+ } while (cursor && !this.disposed);
501
+ const batch = [];
502
+ const reconcileCreatedBatch = async () => {
503
+ if (!batch.length || this.disposed)
504
+ return;
505
+ const batchSize = batch.length;
506
+ progress?.report({
507
+ current: processed,
508
+ total,
509
+ message: `Querying ${batch.length} vault files against metadata index (${processed} of ${total})`,
510
+ });
511
+ const batchSpan = this.app.telemetry.startSpan("metadata.reconcile.batch", {
512
+ parent: reconcileSpan,
513
+ attributes: {
514
+ "metadata.batch.kind": "vault",
515
+ "metadata.batch.limit": batchSize,
516
+ },
517
+ });
518
+ summary.batches += 1;
519
+ let manifest;
520
+ try {
521
+ manifest = await this.app.appDatabase.listIndexedFileManifest({
522
+ paths: batch.map((file) => file.path),
523
+ limit: batch.length,
524
+ });
525
+ batchSpan.end({ "metadata.batch.rows": manifest.rows.length });
526
+ }
527
+ catch (error) {
528
+ batchSpan.recordException(error);
529
+ batchSpan.end();
530
+ throw error;
531
+ }
532
+ const indexedPaths = new Set(manifest.rows.map((file) => file.path));
533
+ for (const file of batch) {
534
+ if (this.disposed)
535
+ return;
536
+ if (!indexedPaths.has(file.path)) {
537
+ await reportProcessing(file.path);
538
+ await this.processFile(file, (stage) => reportProcessing(file.path, stage));
539
+ summary.changed += 1;
540
+ await report(file.path);
541
+ }
542
+ }
543
+ batch.length = 0;
544
+ };
545
+ for (const file of this.app.vault.iterateFiles()) {
546
+ if (this.disposed)
547
+ return summary;
548
+ if (!this.processors.has(file.extension.toLowerCase()))
549
+ continue;
550
+ batch.push(file);
551
+ if (batch.length >= 500)
552
+ await reconcileCreatedBatch();
553
+ }
554
+ await reconcileCreatedBatch();
555
+ }
556
+ catch (error) {
557
+ reconcileSpan.recordException(error);
558
+ throw error;
559
+ }
560
+ finally {
561
+ summary.total = total;
562
+ summary.processed = processed;
563
+ reconcileSpan.end({
564
+ "metadata.files.total": summary.total,
565
+ "metadata.files.processed": summary.processed,
566
+ "metadata.files.changed": summary.changed,
567
+ "metadata.files.deleted": summary.deleted,
568
+ "metadata.batch.count": summary.batches,
569
+ });
570
+ this.reconcileIndexChangeDepth = Math.max(0, this.reconcileIndexChangeDepth - 1);
571
+ if (this.reconcileIndexChangeDepth === 0) {
572
+ this.flushDeferredReconcileIndexChange();
573
+ }
574
+ }
575
+ return summary;
576
+ }
577
+ countProcessableVaultFiles() {
578
+ let total = 0;
579
+ for (const file of this.app.vault.iterateFiles()) {
580
+ if (this.processors.has(file.extension.toLowerCase())) {
581
+ total += 1;
582
+ }
583
+ }
584
+ return total;
585
+ }
586
+ /**
587
+ * Return the current vault/parser manifest fingerprint used to validate the
588
+ * durable metadata index and disposable indexed-consumer snapshots.
589
+ */
590
+ getReconciliationFingerprint() {
591
+ const fingerprint = new StreamingManifestFingerprint();
592
+ for (const file of this.app.vault.iterateFiles()) {
593
+ if (!this.processors.has(file.extension.toLowerCase()))
594
+ continue;
595
+ fingerprint.add([
596
+ file.path,
597
+ file.stat.mtime,
598
+ file.stat.size,
599
+ this.parserSignature(file),
600
+ ].join("\u0000"));
601
+ }
602
+ return fingerprint.finish("metadata-cache-v1");
603
+ }
604
+ markReconcileCheckpointDirty() {
605
+ this.reconcileCheckpointDirty = true;
606
+ this.reconcileCheckpointGeneration += 1;
607
+ }
608
+ async persistReconcileCheckpoint(options = {}) {
609
+ if (this.reconcileCheckpointWrite) {
610
+ await this.reconcileCheckpointWrite;
611
+ return;
612
+ }
613
+ if ((!options.allowDisposed && this.disposed) ||
614
+ !this.reconcileCheckpointReady ||
615
+ !this.reconcileCheckpointDirty ||
616
+ this.activeReconcileMutations > 0) {
617
+ return;
618
+ }
619
+ this.reconcileCheckpointWrite = (async () => {
620
+ while ((options.allowDisposed || !this.disposed) &&
621
+ this.reconcileCheckpointReady &&
622
+ this.reconcileCheckpointDirty &&
623
+ this.activeReconcileMutations === 0) {
624
+ const generation = this.reconcileCheckpointGeneration;
625
+ const fingerprint = this.getReconciliationFingerprint();
626
+ try {
627
+ await this.app.appDatabase.setMeta(METADATA_RECONCILE_CHECKPOINT_KEY, {
628
+ version: METADATA_RECONCILE_CHECKPOINT_VERSION,
629
+ fingerprint,
630
+ completedAt: Date.now(),
631
+ });
632
+ }
633
+ catch (error) {
634
+ this.reconcileCheckpointReady = false;
635
+ this.logger.warn("Failed to persist metadata reconciliation checkpoint", error);
636
+ return;
637
+ }
638
+ if (generation === this.reconcileCheckpointGeneration) {
639
+ this.reconcileCheckpointDirty = false;
640
+ }
641
+ }
642
+ })();
643
+ try {
644
+ await this.reconcileCheckpointWrite;
645
+ }
646
+ finally {
647
+ this.reconcileCheckpointWrite = null;
648
+ }
649
+ }
650
+ async invalidateReconcileCheckpoint() {
651
+ this.reconcileCheckpointReady = false;
652
+ this.markReconcileCheckpointDirty();
653
+ await this.app.appDatabase.setMeta(METADATA_RECONCILE_CHECKPOINT_KEY, {
654
+ version: METADATA_RECONCILE_CHECKPOINT_VERSION,
655
+ fingerprint: "",
656
+ completedAt: 0,
657
+ });
658
+ }
659
+ trackReconcileMutation(operation, completed = () => true) {
660
+ this.markReconcileCheckpointDirty();
661
+ this.activeReconcileMutations += 1;
662
+ const tracked = operation
663
+ .then((value) => {
664
+ if (!completed(value))
665
+ this.reconcileCheckpointReady = false;
666
+ return value;
667
+ })
668
+ .catch((error) => {
669
+ this.reconcileCheckpointReady = false;
670
+ throw error;
671
+ })
672
+ .finally(() => {
673
+ this.activeReconcileMutations = Math.max(0, this.activeReconcileMutations - 1);
674
+ void this.persistReconcileCheckpoint();
675
+ });
676
+ this.trackOperation(tracked);
677
+ }
678
+ getFileCache(file) {
679
+ return this.getCache(file.path);
680
+ }
681
+ getCache(path) {
682
+ const entry = this.fileCache[path];
683
+ const cache = this.metadataCache[entry?.hash];
684
+ if (cache)
685
+ this.touchHotPath(path);
686
+ return cache ?? null;
687
+ }
688
+ async getFileCacheAsync(file) {
689
+ const path = typeof file === "string" ? file : file.path;
690
+ const hot = this.getCache(path);
691
+ if (hot)
692
+ return hot;
693
+ const row = await this.app.appDatabase.getIndexedFile(path);
694
+ if (!row?.metadata)
695
+ return null;
696
+ return this.cacheDatabaseRow(row);
697
+ }
698
+ queryMetadataPage(query = {}) {
699
+ return this.app.appDatabase.queryIndexedMetadataPage(query);
700
+ }
701
+ queryMetadata(query = {}) {
702
+ return this.app.appDatabase.queryIndexedMetadata(query);
703
+ }
704
+ queryFacets(query) {
705
+ return this.app.appDatabase.queryMetadataFacets(query);
706
+ }
707
+ queryLinks(query) {
708
+ return this.app.appDatabase.queryMetadataLinks(query);
709
+ }
710
+ watchQuery(query, listener, onError) {
711
+ let current;
712
+ let generation = 0;
713
+ let disposed = false;
714
+ const refresh = async () => {
715
+ const requestGeneration = ++generation;
716
+ try {
717
+ const page = await this.queryMetadataPage(query);
718
+ if (disposed || requestGeneration !== generation)
719
+ return;
720
+ current = page;
721
+ listener(page);
722
+ }
723
+ catch (error) {
724
+ if (disposed || requestGeneration !== generation)
725
+ return;
726
+ onError?.(error);
727
+ if (!onError)
728
+ throw error;
729
+ }
730
+ };
731
+ const scheduleRefresh = () => {
732
+ void refresh().catch((error) => {
733
+ this.logger.warn("Metadata query watch failed", error);
734
+ });
735
+ };
736
+ const event = this.on("index-changed", (change) => {
737
+ if (change.reset || change.domains.includes("metadata"))
738
+ scheduleRefresh();
739
+ });
740
+ scheduleRefresh();
741
+ return {
742
+ get current() {
743
+ return current;
744
+ },
745
+ refresh,
746
+ dispose: () => {
747
+ disposed = true;
748
+ generation += 1;
749
+ this.offref(event);
750
+ },
751
+ };
752
+ }
753
+ async acquireMetadataSnapshotLease() {
754
+ this.snapshotLeaseCount += 1;
755
+ try {
756
+ if (!this.snapshotLeaseValue) {
757
+ this.snapshotLeasePromise ??= this.buildCompatibilitySnapshot().finally(() => {
758
+ this.snapshotLeasePromise = null;
759
+ });
760
+ this.snapshotLeaseValue = await this.snapshotLeasePromise;
761
+ this.applySnapshot(this.snapshotLeaseValue);
762
+ }
763
+ }
764
+ catch (error) {
765
+ this.snapshotLeaseCount = Math.max(0, this.snapshotLeaseCount - 1);
766
+ if (this.snapshotLeaseCount === 0)
767
+ this.snapshotLeaseValue = null;
768
+ throw error;
769
+ }
770
+ const snapshot = this.snapshotLeaseValue;
771
+ let released = false;
772
+ return {
773
+ snapshot,
774
+ release: () => {
775
+ if (released)
776
+ return;
777
+ released = true;
778
+ this.snapshotLeaseCount = Math.max(0, this.snapshotLeaseCount - 1);
779
+ if (this.snapshotLeaseCount === 0) {
780
+ this.snapshotLeaseValue = null;
781
+ this.trimToHotPaths();
782
+ }
783
+ },
784
+ };
785
+ }
786
+ getDirectReferencePaths(sourcePath) {
787
+ const cache = this.getCache(sourcePath);
788
+ if (!cache) {
789
+ return [];
790
+ }
791
+ const targets = new Set();
792
+ iterateCacheRefs(cache, (ref) => {
793
+ const target = this.getFirstLinkpathDest(ref.link, sourcePath);
794
+ if (target?.path) {
795
+ targets.add(target.path);
796
+ }
797
+ });
798
+ return uniquePaths(targets);
799
+ }
800
+ pathDirectlyReferences(sourcePath, targetPath) {
801
+ if (!sourcePath || !targetPath) {
802
+ return false;
803
+ }
804
+ return this.getDirectReferencePaths(sourcePath).includes(targetPath);
805
+ }
806
+ getDirectReferencingPaths(targetPath) {
807
+ if (!targetPath) {
808
+ return [];
809
+ }
810
+ const matches = [];
811
+ for (const sourcePath of Object.keys(this.fileCache)) {
812
+ if (this.pathDirectlyReferences(sourcePath, targetPath)) {
813
+ matches.push(sourcePath);
814
+ }
815
+ }
816
+ return uniquePaths(matches);
817
+ }
818
+ isDirectlyAffectedByPathChange(watchedPath, changedPath) {
819
+ if (!watchedPath || !changedPath) {
820
+ return false;
821
+ }
822
+ return (watchedPath === changedPath ||
823
+ this.pathDirectlyReferences(watchedPath, changedPath) ||
824
+ this.pathDirectlyReferences(changedPath, watchedPath));
825
+ }
826
+ getFirstLinkpathDest(linkpath, sourcePath) {
827
+ const parsed = parseLinktext(linkpath);
828
+ const normalizedPath = normalizeVaultPath(parsed.path);
829
+ if (!normalizedPath.length) {
830
+ return null;
831
+ }
832
+ const sourceDir = normalizeVaultPath(sourcePath ? dirname(normalizeVaultPath(sourcePath)) : "");
833
+ const relativePath = sourceDir.length
834
+ ? normalizeVaultPath(resolvePath(sourceDir, normalizedPath))
835
+ : normalizedPath;
836
+ const linkName = basename(normalizedPath);
837
+ const basenamePath = linkName.replace(/\.(md|markdown)$/i, "");
838
+ const candidates = [
839
+ relativePath,
840
+ `${relativePath}.md`,
841
+ `${relativePath}.markdown`,
842
+ normalizedPath,
843
+ `${normalizedPath}.md`,
844
+ `${normalizedPath}.markdown`,
845
+ ...this.app.vault
846
+ .getMarkdownFiles()
847
+ .filter((file) => file.basename === basenamePath ||
848
+ file.baseName === basenamePath ||
849
+ file.path === normalizedPath)
850
+ .map((file) => file.path),
851
+ ...this.app.vault
852
+ .getFiles()
853
+ .filter((file) => {
854
+ const filePath = normalizeVaultPath(file.path);
855
+ return (filePath.endsWith(`/${normalizedPath}`) ||
856
+ filePath.endsWith(`/${normalizedPath}.md`) ||
857
+ filePath.endsWith(`/${normalizedPath}.markdown`));
858
+ })
859
+ .map((file) => file.path),
860
+ ...this.app.vault
861
+ .getFiles()
862
+ .filter((file) => file.baseName === linkName)
863
+ .map((file) => file.path),
864
+ ];
865
+ const dedupedCandidates = [...new Set(candidates.filter(Boolean))];
866
+ for (const candidate of dedupedCandidates) {
867
+ const file = this.app.vault.getFileByPath(candidate);
868
+ if (file)
869
+ return file;
870
+ }
871
+ return null;
872
+ }
873
+ fileToLinktext(file, sourcePath, omitMdExtension) {
874
+ const vaultIndex = {
875
+ getFiles: () => this.app.vault.getFiles().map((vaultFile) => ({
876
+ path: vaultFile.path,
877
+ basename: vaultFile.basename,
878
+ extension: vaultFile.extension,
879
+ })),
880
+ };
881
+ return getLinkPath(file.path, sourcePath, {
882
+ ...defaultLinkSettings,
883
+ omitMarkdownExtension: omitMdExtension ?? defaultLinkSettings.omitMarkdownExtension,
884
+ }, vaultIndex);
885
+ }
886
+ rebuild(progress) {
887
+ if (progress) {
888
+ return this.performRebuild(progress);
889
+ }
890
+ return this.app.notifications.withProgress({
891
+ title: "Rebuilding metadata cache",
892
+ source: "Metadata",
893
+ location: "status",
894
+ cancellable: true,
895
+ persistOnError: true,
896
+ }, (handle) => this.performRebuild(handle));
897
+ }
898
+ async performRebuild(progress) {
899
+ await this.invalidateReconcileCheckpoint();
900
+ clearObject(this.fileCache);
901
+ clearObject(this.metadataCache);
902
+ clearObject(this.resolvedLinks);
903
+ clearObject(this.unresolvedLinks);
904
+ this.hotPathOrder.clear();
905
+ let manifestCursor;
906
+ do {
907
+ const page = await this.app.appDatabase.listIndexedFileManifest({
908
+ after: manifestCursor,
909
+ limit: 500,
910
+ });
911
+ for (const indexed of page.rows) {
912
+ if (this.disposed)
913
+ return;
914
+ progress.throwIfCancellationRequested();
915
+ await this.mutateDatabasePaths([indexed.path], () => this.app.appDatabase.deleteIndexedFile(indexed.path));
916
+ }
917
+ manifestCursor = page.nextCursor;
918
+ } while (manifestCursor && !this.disposed);
919
+ const files = this.app.vault.getFiles();
920
+ let processed = 0;
921
+ for (const file of files) {
922
+ if (this.disposed)
923
+ return;
924
+ progress.throwIfCancellationRequested();
925
+ progress.report({
926
+ current: processed,
927
+ total: files.length,
928
+ message: file.path,
929
+ });
930
+ await this.processFile(file);
931
+ processed += 1;
932
+ await yieldToUi();
933
+ }
934
+ if (this.disposed)
935
+ return;
936
+ progress.report({
937
+ current: processed,
938
+ total: files.length,
939
+ message: "Metadata index rebuilt",
940
+ });
941
+ this.reconcileCheckpointReady = true;
942
+ this.markReconcileCheckpointDirty();
943
+ await this.persistReconcileCheckpoint();
944
+ }
945
+ addProcessor(ext, processor) {
946
+ if (!ext?.trim())
947
+ return;
948
+ ext = ext.trim().toLowerCase();
949
+ if (!this.processors.has(ext)) {
950
+ this.processors.set(ext, new Set());
951
+ }
952
+ this.processors.get(ext).add(processor);
953
+ }
954
+ removeProcessor(ext, processor) {
955
+ if (!ext?.trim())
956
+ return false;
957
+ ext = ext.trim().toLowerCase();
958
+ if (!this.processors.has(ext))
959
+ return false;
960
+ return this.processors.get(ext).delete(processor);
961
+ }
962
+ async projectRegisteredIndexes(file, content, cache, hash) {
963
+ const fileRef = {
964
+ path: file.path,
965
+ extension: file.extension,
966
+ name: file.name,
967
+ };
968
+ for (const entry of this.app.indexProjections.matching(fileRef, cache)) {
969
+ try {
970
+ const result = await entry.registration.project({
971
+ file: fileRef,
972
+ content,
973
+ cache,
974
+ });
975
+ await this.app.appDatabase.replaceProjectionSource({
976
+ projectionId: entry.projectionId,
977
+ sourcePath: file.path,
978
+ sourceHash: hash,
979
+ rows: result.rows,
980
+ edges: result.edges,
981
+ });
982
+ }
983
+ catch (error) {
984
+ await this.app.appDatabase.markProjectionSourceError({
985
+ projectionId: entry.projectionId,
986
+ sourcePath: file.path,
987
+ sourceHash: hash,
988
+ error: error instanceof Error ? error.message : String(error),
989
+ });
990
+ }
991
+ }
992
+ }
993
+ addIndexedProjectionContributor(contributor) {
994
+ this.projectionContributors.add(contributor);
995
+ }
996
+ removeIndexedProjectionContributor(contributor) {
997
+ return this.projectionContributors.delete(contributor);
998
+ }
999
+ parserSignature(file) {
1000
+ const processorCount = this.processors.get(file.extension.toLowerCase())?.size ?? 0;
1001
+ return `metadata-cache-v2:${file.extension.toLowerCase()}:${processorCount}`;
1002
+ }
1003
+ cacheDatabaseRow(row) {
1004
+ if (!row.metadata || !isRecord(row.metadata.metadata))
1005
+ return null;
1006
+ const cache = row.metadata.metadata;
1007
+ this.#fileCache[row.file.path] = {
1008
+ mtime: row.file.mtime,
1009
+ size: row.file.size,
1010
+ hash: row.metadata.hash,
1011
+ };
1012
+ this.#metadataCache[row.metadata.hash] = cache;
1013
+ this.#resolvedLinks[row.file.path] = {};
1014
+ this.#unresolvedLinks[row.file.path] = {};
1015
+ for (const link of row.links) {
1016
+ const target = link.resolvedTargetPath;
1017
+ const collection = target
1018
+ ? this.#resolvedLinks[row.file.path]
1019
+ : this.#unresolvedLinks[row.file.path];
1020
+ const key = target ?? link.targetText;
1021
+ collection[key] = (collection[key] ?? 0) + Math.max(1, link.count);
1022
+ }
1023
+ this.touchHotPath(row.file.path);
1024
+ return cache;
1025
+ }
1026
+ touchHotPath(path) {
1027
+ this.hotPathOrder.delete(path);
1028
+ this.hotPathOrder.set(path, true);
1029
+ if (this.snapshotLeaseCount > 0)
1030
+ return;
1031
+ while (this.hotPathOrder.size > METADATA_CACHE_HOT_LIMIT) {
1032
+ const oldest = this.hotPathOrder.keys().next().value;
1033
+ if (!oldest)
1034
+ break;
1035
+ this.evictHotPath(oldest);
1036
+ }
1037
+ }
1038
+ evictHotPath(path) {
1039
+ const hash = this.#fileCache[path]?.hash;
1040
+ delete this.#fileCache[path];
1041
+ delete this.#resolvedLinks[path];
1042
+ delete this.#unresolvedLinks[path];
1043
+ this.hotPathOrder.delete(path);
1044
+ if (hash &&
1045
+ !Object.values(this.#fileCache).some((entry) => entry.hash === hash)) {
1046
+ delete this.#metadataCache[hash];
1047
+ }
1048
+ }
1049
+ trimToHotPaths() {
1050
+ for (const path of Object.keys(this.#fileCache)) {
1051
+ if (!this.hotPathOrder.has(path))
1052
+ this.evictHotPath(path);
1053
+ }
1054
+ while (this.hotPathOrder.size > METADATA_CACHE_HOT_LIMIT) {
1055
+ const oldest = this.hotPathOrder.keys().next().value;
1056
+ if (!oldest)
1057
+ break;
1058
+ this.evictHotPath(oldest);
1059
+ }
1060
+ }
1061
+ handleDatabaseChange(change) {
1062
+ if (this.disposed)
1063
+ return;
1064
+ if (this.snapshotLeaseCount > 0) {
1065
+ this.trackOperation(this.refreshCompatibilitySnapshot(change));
1066
+ }
1067
+ else if (change.reset) {
1068
+ for (const path of [...this.hotPathOrder.keys()]) {
1069
+ if (!this.localMutationPaths.has(path))
1070
+ this.evictHotPath(path);
1071
+ }
1072
+ }
1073
+ else if (change.domains.includes("metadata")) {
1074
+ for (const path of change.paths) {
1075
+ if (!this.localMutationPaths.has(path))
1076
+ this.evictHotPath(path);
1077
+ }
1078
+ }
1079
+ if (this.reconcileIndexChangeDepth > 0 &&
1080
+ change.domains.includes("metadata")) {
1081
+ this.deferReconcileIndexChange(change);
1082
+ return;
1083
+ }
1084
+ this.trigger("index-changed", change);
1085
+ }
1086
+ deferReconcileIndexChange(change) {
1087
+ if (!this.deferredReconcileIndexChange) {
1088
+ this.deferredReconcileIndexChange = cloneAppDatabaseChange(change);
1089
+ return;
1090
+ }
1091
+ const current = this.deferredReconcileIndexChange;
1092
+ this.deferredReconcileIndexChange = {
1093
+ revision: Math.max(current.revision, change.revision),
1094
+ domains: [...new Set([...current.domains, ...change.domains])],
1095
+ paths: [...new Set([...current.paths, ...change.paths])],
1096
+ renamed: [
1097
+ ...(current.renamed ?? []),
1098
+ ...(change.renamed?.map((entry) => ({ ...entry })) ?? []),
1099
+ ],
1100
+ reset: current.reset || change.reset,
1101
+ committedAt: Math.max(current.committedAt, change.committedAt),
1102
+ };
1103
+ }
1104
+ flushDeferredReconcileIndexChange() {
1105
+ const change = this.deferredReconcileIndexChange;
1106
+ this.deferredReconcileIndexChange = null;
1107
+ if (!change || this.disposed)
1108
+ return;
1109
+ this.trigger("index-changed", change);
1110
+ }
1111
+ async refreshCompatibilitySnapshot(change) {
1112
+ if (this.disposed || this.snapshotLeaseCount === 0)
1113
+ return;
1114
+ if (change.reset) {
1115
+ const snapshot = await this.buildCompatibilitySnapshot();
1116
+ if (this.disposed || this.snapshotLeaseCount === 0)
1117
+ return;
1118
+ this.snapshotLeaseValue = snapshot;
1119
+ this.applySnapshot(snapshot);
1120
+ return;
1121
+ }
1122
+ if (!change.domains.includes("metadata"))
1123
+ return;
1124
+ for (const path of change.paths) {
1125
+ if (this.disposed || this.snapshotLeaseCount === 0)
1126
+ return;
1127
+ if (this.localMutationPaths.has(path))
1128
+ continue;
1129
+ const row = await this.app.appDatabase.getIndexedFile(path);
1130
+ if (row?.metadata)
1131
+ this.cacheDatabaseRow(row);
1132
+ else
1133
+ this.evictHotPath(path);
1134
+ }
1135
+ }
1136
+ async mutateDatabasePaths(paths, operation) {
1137
+ paths.forEach((path) => this.localMutationPaths.add(path));
1138
+ try {
1139
+ return await operation();
1140
+ }
1141
+ finally {
1142
+ paths.forEach((path) => this.localMutationPaths.delete(path));
1143
+ }
1144
+ }
1145
+ async buildCompatibilitySnapshot() {
1146
+ const snapshot = {
1147
+ fileCache: {},
1148
+ metadataCache: {},
1149
+ resolvedLinks: {},
1150
+ unresolvedLinks: {},
1151
+ };
1152
+ let cursor;
1153
+ do {
1154
+ const page = await this.app.appDatabase.queryIndexedMetadataPage({
1155
+ after: cursor,
1156
+ limit: 500,
1157
+ });
1158
+ for (const row of page.rows) {
1159
+ if (!row.metadata || !isRecord(row.metadata.metadata))
1160
+ continue;
1161
+ snapshot.fileCache[row.file.path] = {
1162
+ mtime: row.file.mtime,
1163
+ size: row.file.size,
1164
+ hash: row.metadata.hash,
1165
+ };
1166
+ snapshot.metadataCache[row.metadata.hash] = row.metadata.metadata;
1167
+ snapshot.resolvedLinks[row.file.path] = {};
1168
+ snapshot.unresolvedLinks[row.file.path] = {};
1169
+ for (const link of row.links) {
1170
+ const target = link.resolvedTargetPath;
1171
+ const collection = target
1172
+ ? snapshot.resolvedLinks[row.file.path]
1173
+ : snapshot.unresolvedLinks[row.file.path];
1174
+ const key = target ?? link.targetText;
1175
+ collection[key] = (collection[key] ?? 0) + Math.max(1, link.count);
1176
+ }
1177
+ }
1178
+ cursor = page.nextCursor;
1179
+ } while (cursor);
1180
+ return snapshot;
1181
+ }
1182
+ handleChange(event, file) {
1183
+ if (this.disposed)
1184
+ return;
1185
+ if (!(file instanceof TFile))
1186
+ return;
1187
+ switch (event) {
1188
+ case "create":
1189
+ case "modify":
1190
+ if (!this.processors.has(file.extension.toLowerCase()))
1191
+ break;
1192
+ this.trackReconcileMutation(this.processFile(file), (processed) => processed);
1193
+ break;
1194
+ case "delete":
1195
+ const existing = this.fileCache[file.path];
1196
+ this.trigger("deleted", file, existing ? (this.metadataCache[existing.hash] ?? null) : null);
1197
+ this.evictHotPath(file.path);
1198
+ this.trackReconcileMutation(this.mutateDatabasePaths([file.path], () => this.app.appDatabase.deleteIndexedFile(file.path)));
1199
+ break;
1200
+ }
1201
+ }
1202
+ handleRename(file, oldPath) {
1203
+ if (this.disposed)
1204
+ return;
1205
+ if (!(file instanceof TFile))
1206
+ return;
1207
+ const existing = this.fileCache[oldPath];
1208
+ if (existing) {
1209
+ this.fileCache[file.path] = {
1210
+ ...existing,
1211
+ mtime: file.stat.mtime,
1212
+ size: file.stat.size,
1213
+ };
1214
+ delete this.fileCache[oldPath];
1215
+ if (this.resolvedLinks[oldPath]) {
1216
+ this.resolvedLinks[file.path] = this.resolvedLinks[oldPath];
1217
+ delete this.resolvedLinks[oldPath];
1218
+ }
1219
+ if (this.unresolvedLinks[oldPath]) {
1220
+ this.unresolvedLinks[file.path] = this.unresolvedLinks[oldPath];
1221
+ delete this.unresolvedLinks[oldPath];
1222
+ }
1223
+ this.hotPathOrder.delete(oldPath);
1224
+ this.touchHotPath(file.path);
1225
+ }
1226
+ this.trackReconcileMutation(this.mutateDatabasePaths([oldPath, file.path], () => this.app.appDatabase.renameIndexedFile(oldPath, file.path)));
1227
+ }
1228
+ trackOperation(operation) {
1229
+ this.pendingOperations.add(operation);
1230
+ void operation
1231
+ .catch((error) => {
1232
+ this.logger.warn("Metadata cache background operation failed", error);
1233
+ })
1234
+ .finally(() => {
1235
+ this.pendingOperations.delete(operation);
1236
+ });
1237
+ }
1238
+ async processFile(file, onStage) {
1239
+ if (this.disposed || !file)
1240
+ return false;
1241
+ file = this.app.vault.getFileByPath(file.path);
1242
+ if (this.disposed || !file)
1243
+ return false;
1244
+ const processors = this.processors.get(file.extension.toLowerCase());
1245
+ if (!processors || !processors.size)
1246
+ return false;
1247
+ await onStage?.("Reading");
1248
+ const content = await this.app.vault.read(file);
1249
+ if (this.disposed)
1250
+ return false;
1251
+ await onStage?.("Hashing");
1252
+ const hash = md5(content);
1253
+ const existing = this.fileCache[file.path];
1254
+ if (existing && existing.hash === hash)
1255
+ return false;
1256
+ await onStage?.("Parsing metadata");
1257
+ let cachedMetadata = await this.read(content, file);
1258
+ if (this.disposed)
1259
+ return false;
1260
+ if (cachedMetadata) {
1261
+ this.#metadataCache[hash] = cachedMetadata;
1262
+ this.#fileCache[file.path] = {
1263
+ mtime: file.stat.mtime,
1264
+ size: file.stat.size,
1265
+ hash,
1266
+ };
1267
+ this.touchHotPath(file.path);
1268
+ this.processLink(file);
1269
+ const record = this.toDatabaseRecord(file, hash, cachedMetadata);
1270
+ await onStage?.("Writing metadata index");
1271
+ await this.mutateDatabasePaths([file.path], () => this.app.appDatabase.upsertIndexedFile(record));
1272
+ await onStage?.("Writing projection index");
1273
+ await this.projectRegisteredIndexes(file, content, cachedMetadata, hash);
1274
+ await onStage?.("Notifying metadata listeners");
1275
+ this.trigger("changed", file, content, cachedMetadata);
1276
+ }
1277
+ if (existing && existing.hash !== hash) {
1278
+ if (!Object.values(this.fileCache).some((entry) => entry.hash === existing.hash)) {
1279
+ delete this.metadataCache[existing.hash];
1280
+ }
1281
+ }
1282
+ this.logger.debug("Processing file", file.path);
1283
+ return Boolean(cachedMetadata);
1284
+ }
1285
+ extractLink(path) {
1286
+ const [segment, description] = path.split("|", 2);
1287
+ const [url, sectionId] = segment.split("#", 2);
1288
+ return {
1289
+ url,
1290
+ sectionId,
1291
+ displayText: description || basename(path) + (sectionId ? ` > ${sectionId}` : ""),
1292
+ };
1293
+ }
1294
+ processLink(file) {
1295
+ const cachedMetadata = this.metadataCache[this.fileCache[file.path]?.hash];
1296
+ if (!cachedMetadata)
1297
+ return;
1298
+ this.resolvedLinks[file.path] = {};
1299
+ this.unresolvedLinks[file.path] = {};
1300
+ const links = cachedMetadata.links;
1301
+ if (!links?.length)
1302
+ return;
1303
+ links.forEach((link) => {
1304
+ const spec = this.extractLink(link.link);
1305
+ const path = ([...this.processors.keys()]
1306
+ .map((ext) => `${spec.url}.${ext}`)
1307
+ .find((path) => this.app.vault.getFileByPath(path)) ??
1308
+ this.app.vault.getFileByPath(spec.url))
1309
+ ? spec.url
1310
+ : null;
1311
+ if (path) {
1312
+ this.resolvedLinks[file.path][path] ||= 0;
1313
+ this.resolvedLinks[file.path][path] += 1;
1314
+ }
1315
+ else {
1316
+ this.unresolvedLinks[file.path][link.link] ||= 0;
1317
+ this.unresolvedLinks[file.path][link.link] += 1;
1318
+ }
1319
+ });
1320
+ }
1321
+ toDatabaseRecord(file, hash, cache) {
1322
+ const refs = [];
1323
+ (cache.links ?? []).forEach((ref, ordinal) => {
1324
+ const spec = this.extractLink(ref.link);
1325
+ refs.push({
1326
+ sourcePath: file.path,
1327
+ targetText: ref.link,
1328
+ original: ref.original,
1329
+ resolvedTargetPath: this.getFirstLinkpathDest(spec.url, file.path)?.path ?? null,
1330
+ type: "link",
1331
+ position: ref.position,
1332
+ count: 1,
1333
+ heading: ref.heading ?? null,
1334
+ kind: "reference",
1335
+ ordinal,
1336
+ });
1337
+ });
1338
+ for (const ref of cache.embeds ?? []) {
1339
+ const spec = this.extractLink(ref.link);
1340
+ refs.push({
1341
+ sourcePath: file.path,
1342
+ targetText: ref.link,
1343
+ original: ref.original,
1344
+ resolvedTargetPath: this.getFirstLinkpathDest(spec.url, file.path)?.path ?? null,
1345
+ type: "embed",
1346
+ position: ref.position,
1347
+ count: 1,
1348
+ });
1349
+ }
1350
+ const inlineTags = new Map((cache.tags ?? []).map((tag) => [tag.tag, tag.position]));
1351
+ const tagNames = new Set([
1352
+ ...inlineTags.keys(),
1353
+ ...(parseFrontMatterTags(cache.frontmatter) ?? []),
1354
+ ]);
1355
+ const tags = [...tagNames].map((tag) => {
1356
+ const parts = tagParts(tag);
1357
+ return {
1358
+ path: file.path,
1359
+ tag,
1360
+ parts: parts.parts,
1361
+ hierarchy: parts.hierarchy,
1362
+ position: inlineTags.get(tag),
1363
+ };
1364
+ });
1365
+ const properties = Object.entries(cache.frontmatter ?? {}).map(([name, value]) => ({
1366
+ path: file.path,
1367
+ name,
1368
+ inferredType: this.app.metadataTypeManager?.determinePropertyType(name, value) ??
1369
+ frontmatterValueType(value),
1370
+ declaredType: this.app.metadataTypeManager?.types?.[name]?.type,
1371
+ value,
1372
+ }));
1373
+ return {
1374
+ file: {
1375
+ path: file.path,
1376
+ normalizedPath: file.path,
1377
+ extension: file.extension.toLowerCase(),
1378
+ mtime: file.stat.mtime,
1379
+ size: file.stat.size,
1380
+ hash,
1381
+ indexed: true,
1382
+ },
1383
+ metadata: {
1384
+ path: file.path,
1385
+ hash,
1386
+ parserVersion: this.parserSignature(file),
1387
+ metadata: cache,
1388
+ },
1389
+ links: refs,
1390
+ tags,
1391
+ properties,
1392
+ };
1393
+ }
1394
+ async processFileCache(file) {
1395
+ if (!file)
1396
+ return null;
1397
+ if (typeof file === "string") {
1398
+ return this.processFileCache(this.app.vault.getFileByPath(file));
1399
+ }
1400
+ return this.read(await this.app.vault.read(file), file);
1401
+ }
1402
+ async read(content, file) {
1403
+ const processors = this.processors.get(file.extension.toLowerCase());
1404
+ if (!processors || !processors.size)
1405
+ return null;
1406
+ let cachedMetadata = {};
1407
+ for (const processor of processors) {
1408
+ const cache = await processor.read(content, {
1409
+ file,
1410
+ cache: cachedMetadata,
1411
+ });
1412
+ if (cache) {
1413
+ cachedMetadata = cache;
1414
+ }
1415
+ }
1416
+ return cachedMetadata;
1417
+ }
1418
+ writeFrontmatter(file, data) {
1419
+ const processors = this.processors.get(file.extension.toLowerCase());
1420
+ if (!processors || !processors.size)
1421
+ return "";
1422
+ for (const processor of processors) {
1423
+ const value = processor.write(data);
1424
+ if (value) {
1425
+ return value;
1426
+ }
1427
+ }
1428
+ return "";
1429
+ }
1430
+ }
1431
+ export function getAllTags(cache) {
1432
+ return (cache.tags || []).map((it) => it.tag);
1433
+ }