@plebeiantech/lat.md 0.12.3-fork.17 → 0.12.3-fork.20

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 (244) hide show
  1. package/README.md +1 -1
  2. package/dist/src/cli/agent-invocation.d.ts +6 -0
  3. package/dist/src/cli/agent-invocation.js +23 -0
  4. package/dist/src/cli/fork-instructions.js +1 -1
  5. package/dist/src/cli/fork-scaffold.js +2 -2
  6. package/dist/src/cli/graph.d.ts +1 -1
  7. package/dist/src/cli/hook.js +11 -11
  8. package/dist/src/cli/index.js +41 -59
  9. package/dist/src/cli/init-preferences.js +5 -4
  10. package/dist/src/cli/init.d.ts +1 -1
  11. package/dist/src/cli/init.js +54 -45
  12. package/dist/src/cli/reindex.d.ts +1 -1
  13. package/dist/src/cli/reindex.js +2 -2
  14. package/dist/src/cli/search.d.ts +4 -4
  15. package/dist/src/cli/search.js +41 -28
  16. package/dist/src/cli/ui-build-server.d.ts +1 -1
  17. package/dist/src/cli/ui-build.d.ts +1 -1
  18. package/dist/src/cli/ui.d.ts +1 -1
  19. package/dist/src/fork/antigravity-hook.js +6 -6
  20. package/dist/src/fork/antigravity-init.d.ts +2 -2
  21. package/dist/src/fork/antigravity-init.js +2 -2
  22. package/dist/src/graph-export.js +7 -7
  23. package/dist/src/knowledge/index.js +2 -2
  24. package/dist/src/mcp/server.js +8 -8
  25. package/dist/src/search/cache.d.ts +1 -1
  26. package/dist/src/search/cache.js +40 -7
  27. package/dist/src/search/chunks.d.ts +2 -2
  28. package/dist/src/search/db.d.ts +5 -4
  29. package/dist/src/search/db.js +36 -40
  30. package/dist/src/search/embedder.js +1 -1
  31. package/dist/src/search/index.d.ts +1 -1
  32. package/dist/src/search/index.js +2 -2
  33. package/dist/src/search/lock.d.ts +4 -1
  34. package/dist/src/search/lock.js +30 -8
  35. package/dist/src/search/query.d.ts +5 -2
  36. package/dist/src/search/query.js +55 -38
  37. package/dist/src/search/search.d.ts +3 -1
  38. package/dist/src/search/search.js +21 -12
  39. package/dist/src/search/types.d.ts +2 -18
  40. package/dist/src/view/client/assets/{GraphView-BWlRdQM4.js → GraphView-Dij8mAA_.js} +1 -1
  41. package/dist/src/view/client/assets/{MarkdownEditor-MW-Ck1O0.js → MarkdownEditor-88Wbt0M1.js} +1 -1
  42. package/dist/src/view/client/assets/{abnfDiagram-VCTEODGH-Bn3RshMV.js → abnfDiagram-VCTEODGH-y0DlYa8E.js} +1 -1
  43. package/dist/src/view/client/assets/{arc-En9tdFQF.js → arc-FlMu8dnO.js} +1 -1
  44. package/dist/src/view/client/assets/{architectureDiagram-5GKGNRK7-B-9J1VAM.js → architectureDiagram-5GKGNRK7-DA_uWZW1.js} +1 -1
  45. package/dist/src/view/client/assets/{blockDiagram-I7D4REHJ-QlznwalP.js → blockDiagram-I7D4REHJ-BHQ5LU85.js} +1 -1
  46. package/dist/src/view/client/assets/{c4Diagram-7LVT6UL2-Bp2y8B4_.js → c4Diagram-7LVT6UL2-BRucKCt2.js} +1 -1
  47. package/dist/src/view/client/assets/channel-BgDNA_Ay.js +1 -0
  48. package/dist/src/view/client/assets/{chunk-2Q5K7J3B-CnBKL-lm.js → chunk-2Q5K7J3B-BJPcFivt.js} +1 -1
  49. package/dist/src/view/client/assets/{chunk-5VM5RSS4-LLYMi_n6.js → chunk-5VM5RSS4-v2q99bGz.js} +1 -1
  50. package/dist/src/view/client/assets/{chunk-F27PBJKO-VIeO7VF0.js → chunk-F27PBJKO-CcHOuJCs.js} +1 -1
  51. package/dist/src/view/client/assets/{chunk-IMKFNOWR-D5i67DVD.js → chunk-IMKFNOWR-DNxYuJNq.js} +1 -1
  52. package/dist/src/view/client/assets/{chunk-JWPE2WC7-D4qAtoL3.js → chunk-JWPE2WC7-C0eHKvLW.js} +1 -1
  53. package/dist/src/view/client/assets/{chunk-POPQ4Y6H-BMzOyiW_.js → chunk-POPQ4Y6H-57CWp2Ve.js} +1 -1
  54. package/dist/src/view/client/assets/{chunk-SVP7TREG-DWy5nbfp.js → chunk-SVP7TREG-BqcoxIJw.js} +1 -1
  55. package/dist/src/view/client/assets/{chunk-TICWLB2K-CVqCANFB.js → chunk-TICWLB2K-DRUJR5zH.js} +1 -1
  56. package/dist/src/view/client/assets/{chunk-XXDRQBXY-Bp23h6Tf.js → chunk-XXDRQBXY-Dez0oRoM.js} +1 -1
  57. package/dist/src/view/client/assets/classDiagram-ZZMXUADV-CFZ_Sgh5.js +1 -0
  58. package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-CFZ_Sgh5.js +1 -0
  59. package/dist/src/view/client/assets/{cose-bilkent-JH36ORCC-BCw7fCC2.js → cose-bilkent-JH36ORCC-BHtud6Hg.js} +1 -1
  60. package/dist/src/view/client/assets/{cynefin-OW5HDTMX--lYN4N9m.js → cynefin-OW5HDTMX-BjBW8VGk.js} +1 -1
  61. package/dist/src/view/client/assets/{cynefinDiagram-5FMLGOSQ-9sK5EV7v.js → cynefinDiagram-5FMLGOSQ-DwGuD7oF.js} +1 -1
  62. package/dist/src/view/client/assets/{dagre-GXQ25YYZ-CoMdEFCq.js → dagre-GXQ25YYZ-C5zSlGK9.js} +1 -1
  63. package/dist/src/view/client/assets/{diagram-S7CK7UJ4-D13dyWdd.js → diagram-S7CK7UJ4-C_NhA3bj.js} +1 -1
  64. package/dist/src/view/client/assets/{diagram-UQ7AKVKN-PdwLPPPA.js → diagram-UQ7AKVKN-az7QPYNb.js} +1 -1
  65. package/dist/src/view/client/assets/{diagram-VSXAHHWV-CtWWa_A7.js → diagram-VSXAHHWV-BRvDuM8_.js} +1 -1
  66. package/dist/src/view/client/assets/{diagram-VX7I27RA-BeCYcSU-.js → diagram-VX7I27RA-BWLgF6S-.js} +1 -1
  67. package/dist/src/view/client/assets/{diagram-Z3DM3KII-DiWabOBM.js → diagram-Z3DM3KII-DV3d6mL5.js} +1 -1
  68. package/dist/src/view/client/assets/{ebnfDiagram-PWID7BFC-CZQ3MD_K.js → ebnfDiagram-PWID7BFC-yUXrEAvK.js} +1 -1
  69. package/dist/src/view/client/assets/{erDiagram-RLTQ6QDP-B1AhXIrP.js → erDiagram-RLTQ6QDP-D35FN1Pt.js} +1 -1
  70. package/dist/src/view/client/assets/{flowDiagram-HODETNUW-Bhr0JM6P.js → flowDiagram-HODETNUW-BciZqsjT.js} +1 -1
  71. package/dist/src/view/client/assets/{ganttDiagram-EL5Y4UJY-CbP8iAkg.js → ganttDiagram-EL5Y4UJY-BysPGgXS.js} +1 -1
  72. package/dist/src/view/client/assets/{gitGraphDiagram-WWUBYQGX-B9uuGdLF.js → gitGraphDiagram-WWUBYQGX-C3DdJ5xG.js} +1 -1
  73. package/dist/src/view/client/assets/index-CJokoRSp.js +13 -0
  74. package/dist/src/view/client/assets/{infoDiagram-27XIBGKW-BrfwQ_Cq.js → infoDiagram-27XIBGKW-D3HZrDRb.js} +1 -1
  75. package/dist/src/view/client/assets/{ishikawaDiagram-5VMMS53U-CHtJ36re.js → ishikawaDiagram-5VMMS53U-BXyVIXTU.js} +1 -1
  76. package/dist/src/view/client/assets/{journeyDiagram-3NMN7TZE-BChcgX1s.js → journeyDiagram-3NMN7TZE-D4-1NxVU.js} +1 -1
  77. package/dist/src/view/client/assets/{kanban-definition-UXKFOSKX-BnwFsKiB.js → kanban-definition-UXKFOSKX-4Gu_5yBA.js} +1 -1
  78. package/dist/src/view/client/assets/{layout-Dnd0pLdb.js → layout-Jf1K3TMM.js} +1 -1
  79. package/dist/src/view/client/assets/{linear-CFhW2ozn.js → linear-DWfwzUT_.js} +1 -1
  80. package/dist/src/view/client/assets/{mermaid.core--OP6lXtW.js → mermaid.core-DMx_DAl8.js} +5 -5
  81. package/dist/src/view/client/assets/{mindmap-definition-YA3MSWOX-D51y2zbm.js → mindmap-definition-YA3MSWOX-8pxY_RWi.js} +1 -1
  82. package/dist/src/view/client/assets/{pegDiagram-XKGWAZYB--zyQJDvI.js → pegDiagram-XKGWAZYB-DNAw_O5t.js} +1 -1
  83. package/dist/src/view/client/assets/{pieDiagram-E7YTZNPT-Cj8mvpJy.js → pieDiagram-E7YTZNPT-uuvRCDkC.js} +1 -1
  84. package/dist/src/view/client/assets/{quadrantDiagram-AXDQQJYC-qvhJrnBf.js → quadrantDiagram-AXDQQJYC-BER2O4Aq.js} +1 -1
  85. package/dist/src/view/client/assets/{railroadDiagram-O6MQD6OU-CtWvxpSo.js → railroadDiagram-O6MQD6OU-CxEsJ_0n.js} +1 -1
  86. package/dist/src/view/client/assets/{requirementDiagram-BXWQKSXE-CBxen_KG.js → requirementDiagram-BXWQKSXE-yS9rspca.js} +1 -1
  87. package/dist/src/view/client/assets/{sankeyDiagram-P5KCCOFB-B_fUkmfc.js → sankeyDiagram-P5KCCOFB-Bqt70a2j.js} +1 -1
  88. package/dist/src/view/client/assets/{sequenceDiagram-WJ2MYXX4-Dclv4p68.js → sequenceDiagram-WJ2MYXX4-BvOOEJNr.js} +1 -1
  89. package/dist/src/view/client/assets/{sizeCapture-INFHLROL-GfG0sMTZ.js → sizeCapture-INFHLROL-CvO_Pjjo.js} +1 -1
  90. package/dist/src/view/client/assets/{stateDiagram-D77RDMKH-YQWPnzxU.js → stateDiagram-D77RDMKH-KLmVOZkq.js} +1 -1
  91. package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-KCS8Mdzu.js +1 -0
  92. package/dist/src/view/client/assets/{swimlanes-42K2YHIH-5yiCveVS.js → swimlanes-42K2YHIH-DC1r4_sq.js} +1 -1
  93. package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-B2TcAwis.js +8 -0
  94. package/dist/src/view/client/assets/{timeline-definition-24CTP7MA-BvAbB5Zs.js → timeline-definition-24CTP7MA-ChXLOuBL.js} +1 -1
  95. package/dist/src/view/client/assets/{vennDiagram-4TSXK5OY-guC-r60n.js → vennDiagram-4TSXK5OY-D9_h25-Y.js} +1 -1
  96. package/dist/src/view/client/assets/{wardleyDiagram-VM6X3IG4-C0yXcU2x.js → wardleyDiagram-VM6X3IG4-CsQJGGW0.js} +1 -1
  97. package/dist/src/view/client/assets/{xychartDiagram-S5SC5T6Z-RPmkPNfK.js → xychartDiagram-S5SC5T6Z-DGMuPMlm.js} +1 -1
  98. package/dist/src/view/client/index.html +1 -1
  99. package/dist/src/view/diagnostics.d.ts +2 -2
  100. package/dist/src/view/diagnostics.js +5 -5
  101. package/dist/src/view/external-document-tree.js +1 -1
  102. package/dist/src/view/git-diff.js +1 -1
  103. package/dist/src/view/git.js +1 -1
  104. package/dist/src/view/graph.d.ts +2 -2
  105. package/dist/src/view/graph.js +2 -2
  106. package/dist/src/view/markdown.js +1 -1
  107. package/dist/src/view/preindexed-search.d.ts +1 -1
  108. package/dist/src/view/protocol.d.ts +2 -18
  109. package/dist/src/view/publication.d.ts +2 -0
  110. package/dist/src/view/publication.js +78 -0
  111. package/dist/src/view/references.d.ts +3 -3
  112. package/dist/src/view/references.js +3 -3
  113. package/dist/src/view/repository.d.ts +6 -6
  114. package/dist/src/view/repository.js +28 -14
  115. package/dist/src/view/request-security.d.ts +5 -0
  116. package/dist/src/view/request-security.js +46 -0
  117. package/dist/src/view/search-result.d.ts +1 -1
  118. package/dist/src/view/search-result.js +1 -1
  119. package/dist/src/view/search.d.ts +1 -1
  120. package/dist/src/view/server-build.d.ts +2 -2
  121. package/dist/src/view/server-build.js +2 -2
  122. package/dist/src/view/server-deployment.d.ts +1 -2
  123. package/dist/src/view/server-deployment.js +6 -46
  124. package/dist/src/view/server-index-worker.d.ts +1 -1
  125. package/dist/src/view/server.d.ts +1 -1
  126. package/dist/src/view/server.js +13 -3
  127. package/dist/src/view/source-target.js +2 -2
  128. package/dist/src/view/static-build.d.ts +1 -1
  129. package/dist/src/view/static-build.js +49 -17
  130. package/dist/src/view/store.d.ts +4 -2
  131. package/dist/src/view/store.js +31 -19
  132. package/dist/src/view/table-of-contents.d.ts +1 -1
  133. package/dist/src/view/table-of-contents.js +1 -1
  134. package/dist/src/view/vercel-server-build.d.ts +1 -1
  135. package/package.json +19 -29
  136. package/templates/AGENTS.md +2 -2
  137. package/templates/cursor-rules.md +2 -2
  138. package/templates/opencode-plugin.ts +9 -9
  139. package/templates/pi-extension.ts +11 -11
  140. package/templates/skill/SKILL.md +2 -1
  141. package/dist/src/cli/check-context.d.ts +0 -56
  142. package/dist/src/cli/check-context.js +0 -215
  143. package/dist/src/cli/check-coverage.d.ts +0 -2
  144. package/dist/src/cli/check-coverage.js +0 -75
  145. package/dist/src/cli/check-frontmatter.d.ts +0 -12
  146. package/dist/src/cli/check-frontmatter.js +0 -44
  147. package/dist/src/cli/check-mode.d.ts +0 -21
  148. package/dist/src/cli/check-mode.js +0 -394
  149. package/dist/src/cli/check-status.d.ts +0 -74
  150. package/dist/src/cli/check-status.js +0 -217
  151. package/dist/src/cli/check.d.ts +0 -52
  152. package/dist/src/cli/check.js +0 -834
  153. package/dist/src/cli/context.d.ts +0 -11
  154. package/dist/src/cli/context.js +0 -58
  155. package/dist/src/cli/expand.d.ts +0 -7
  156. package/dist/src/cli/expand.js +0 -141
  157. package/dist/src/cli/external.d.ts +0 -11
  158. package/dist/src/cli/external.js +0 -185
  159. package/dist/src/cli/gen-index.d.ts +0 -45
  160. package/dist/src/cli/gen-index.js +0 -235
  161. package/dist/src/cli/link-scheme.d.ts +0 -54
  162. package/dist/src/cli/link-scheme.js +0 -101
  163. package/dist/src/cli/locate.d.ts +0 -2
  164. package/dist/src/cli/locate.js +0 -18
  165. package/dist/src/cli/refs.d.ts +0 -23
  166. package/dist/src/cli/refs.js +0 -274
  167. package/dist/src/cli/section.d.ts +0 -45
  168. package/dist/src/cli/section.js +0 -328
  169. package/dist/src/cli/select-menu.d.ts +0 -13
  170. package/dist/src/cli/select-menu.js +0 -100
  171. package/dist/src/code-refs.d.ts +0 -29
  172. package/dist/src/code-refs.js +0 -479
  173. package/dist/src/config.d.ts +0 -29
  174. package/dist/src/config.js +0 -83
  175. package/dist/src/context.d.ts +0 -26
  176. package/dist/src/context.js +0 -11
  177. package/dist/src/document-formats.d.ts +0 -7
  178. package/dist/src/document-formats.js +0 -39
  179. package/dist/src/extensions/alert-marker.d.ts +0 -25
  180. package/dist/src/extensions/alert-marker.js +0 -55
  181. package/dist/src/extensions/wiki-link/from-markdown.d.ts +0 -5
  182. package/dist/src/extensions/wiki-link/from-markdown.js +0 -34
  183. package/dist/src/extensions/wiki-link/index.d.ts +0 -4
  184. package/dist/src/extensions/wiki-link/index.js +0 -3
  185. package/dist/src/extensions/wiki-link/syntax.d.ts +0 -14
  186. package/dist/src/extensions/wiki-link/syntax.js +0 -121
  187. package/dist/src/extensions/wiki-link/to-markdown.d.ts +0 -5
  188. package/dist/src/extensions/wiki-link/to-markdown.js +0 -28
  189. package/dist/src/extensions/wiki-link/types.d.ts +0 -34
  190. package/dist/src/extensions/wiki-link/types.js +0 -1
  191. package/dist/src/external-documents.d.ts +0 -56
  192. package/dist/src/external-documents.js +0 -404
  193. package/dist/src/external-sources.d.ts +0 -136
  194. package/dist/src/external-sources.js +0 -1098
  195. package/dist/src/fork/frontmatter-fields.d.ts +0 -36
  196. package/dist/src/fork/frontmatter-fields.js +0 -23
  197. package/dist/src/format.d.ts +0 -16
  198. package/dist/src/format.js +0 -76
  199. package/dist/src/init-version.d.ts +0 -10
  200. package/dist/src/init-version.js +0 -49
  201. package/dist/src/lattice-model.d.ts +0 -84
  202. package/dist/src/lattice-model.js +0 -467
  203. package/dist/src/lattice.d.ts +0 -14
  204. package/dist/src/lattice.js +0 -353
  205. package/dist/src/markdown-analysis-cache.d.ts +0 -20
  206. package/dist/src/markdown-analysis-cache.js +0 -139
  207. package/dist/src/markdown-analysis-worker.d.ts +0 -16
  208. package/dist/src/markdown-analysis-worker.js +0 -33
  209. package/dist/src/markdown-analysis.d.ts +0 -61
  210. package/dist/src/markdown-analysis.js +0 -169
  211. package/dist/src/markdown-analyzer-loader.d.ts +0 -4
  212. package/dist/src/markdown-analyzer-loader.js +0 -13
  213. package/dist/src/markdown-validation.d.ts +0 -25
  214. package/dist/src/markdown-validation.js +0 -104
  215. package/dist/src/parser-cache.d.ts +0 -17
  216. package/dist/src/parser-cache.js +0 -65
  217. package/dist/src/parser-import.d.ts +0 -8
  218. package/dist/src/parser-import.js +0 -1
  219. package/dist/src/parser.d.ts +0 -3
  220. package/dist/src/parser.js +0 -93
  221. package/dist/src/path.d.ts +0 -2
  222. package/dist/src/path.js +0 -4
  223. package/dist/src/profiler.d.ts +0 -16
  224. package/dist/src/profiler.js +0 -81
  225. package/dist/src/project-analysis.d.ts +0 -59
  226. package/dist/src/project-analysis.js +0 -233
  227. package/dist/src/project-discovery.d.ts +0 -6
  228. package/dist/src/project-discovery.js +0 -30
  229. package/dist/src/repository-path.d.ts +0 -11
  230. package/dist/src/repository-path.js +0 -52
  231. package/dist/src/source-formats.d.ts +0 -7
  232. package/dist/src/source-formats.js +0 -30
  233. package/dist/src/source-parser.d.ts +0 -67
  234. package/dist/src/source-parser.js +0 -1749
  235. package/dist/src/untrusted.d.ts +0 -18
  236. package/dist/src/untrusted.js +0 -93
  237. package/dist/src/view/client/assets/channel-uy0fFvcW.js +0 -1
  238. package/dist/src/view/client/assets/classDiagram-ZZMXUADV-DbSEqjA5.js +0 -1
  239. package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-DbSEqjA5.js +0 -1
  240. package/dist/src/view/client/assets/index-nBmbYfun.js +0 -13
  241. package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-gUj6Hzgl.js +0 -1
  242. package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-DR7JhrxL.js +0 -8
  243. package/dist/src/walk.d.ts +0 -20
  244. package/dist/src/walk.js +0 -121
@@ -1,11 +1,11 @@
1
1
  import { readFile, realpath } from 'node:fs/promises';
2
2
  import { dirname, extname, isAbsolute, join, posix, relative, resolve, sep, } from 'node:path';
3
- import { buildFileIndex, buildSectionSlugIndex, flattenSections, resolveRef, } from '../lattice-model.js';
4
- import { createExternalResolver, } from '../external-sources.js';
5
- import { addExternalDocumentAliasAnchors, externalDocumentSections, } from '../external-documents.js';
6
- import { resolveSourceSymbol, SourceParserRuntime, } from '../source-parser.js';
7
- import { isSourceFileExtension } from '../source-formats.js';
8
- import { toPosix } from '../path.js';
3
+ import { buildFileIndex, buildSectionSlugIndex, flattenSections, resolveRef, } from '@plebeiantech/lat.md-core/lattice-model';
4
+ import { createExternalResolver, } from '@plebeiantech/lat.md-core/external-sources';
5
+ import { addExternalDocumentAliasAnchors, externalDocumentSections, } from '@plebeiantech/lat.md-core/external-documents';
6
+ import { resolveSourceSymbol, SourceParserRuntime, } from '@plebeiantech/lat.md-core/source-parser';
7
+ import { isSourceFileExtension } from '@plebeiantech/lat.md-core/source-formats';
8
+ import { toPosix } from '@plebeiantech/lat.md-core/path';
9
9
  import { renderExternalDocumentTree } from './external-document-tree.js';
10
10
  import { visitDocumentElements } from './document-tree.js';
11
11
  import { highlightSource } from './highlight.js';
@@ -73,7 +73,7 @@ function externalDocumentLink(href, current, external, available) {
73
73
  href.startsWith('?') ||
74
74
  href.startsWith('//') ||
75
75
  /^[a-z][a-z\d+.-]*:/i.test(href) ||
76
- /^\/(?:code|docs|external)(?:\/|$)/.test(href)) {
76
+ /^\/external(?:\/|$)/.test(href)) {
77
77
  return undefined;
78
78
  }
79
79
  let destination;
@@ -168,7 +168,7 @@ function matchingSymbol(symbols, symbolPath) {
168
168
  }
169
169
  return undefined;
170
170
  }
171
- export async function createMarkdownWikiLinkResolver(latDir, requestedPath, loadedSections, referenceIndex, externalResolver) {
171
+ export async function createMarkdownWikiLinkResolver(latDir, requestedPath, loadedSections, referenceIndex, externalResolver, publishable) {
172
172
  const external = externalResolver ?? (await createExternalResolver(latDir, dirname(latDir)));
173
173
  const projectRoot = dirname(latDir);
174
174
  const flat = flattenSections(loadedSections);
@@ -210,7 +210,7 @@ export async function createMarkdownWikiLinkResolver(latDir, requestedPath, load
210
210
  };
211
211
  }
212
212
  const source = viewSourceTarget(target);
213
- if (!source)
213
+ if (!source || (publishable && !(await publishable(source.path))))
214
214
  return null;
215
215
  try {
216
216
  await readViewSource(latDir, projectRoot, source.path, source.symbol);
@@ -236,7 +236,7 @@ export async function createMarkdownWikiLinkResolver(latDir, requestedPath, load
236
236
  };
237
237
  }
238
238
  /** Resolve and render one configured external file through the normal view models. */
239
- export async function getViewExternal(latDir, projectRoot, targetValue, external, allSections, referenceIndex) {
239
+ export async function getViewExternal(latDir, projectRoot, targetValue, external, allSections, referenceIndex, publishable) {
240
240
  let resolved;
241
241
  try {
242
242
  resolved = await external.resolve(targetValue);
@@ -244,12 +244,26 @@ export async function getViewExternal(latDir, projectRoot, targetValue, external
244
244
  catch (error) {
245
245
  throw new ViewExternalNotFoundError(error.message);
246
246
  }
247
- const createResolver = (path) => createMarkdownWikiLinkResolver(latDir, path, allSections, referenceIndex, external);
247
+ const createResolver = (path) => createMarkdownWikiLinkResolver(latDir, path, allSections, referenceIndex, external, publishable);
248
248
  if (resolved.kind === 'document') {
249
249
  const virtualPath = join(projectRoot, 'lat.md', '.external', resolved.target.handle, resolved.target.resolvedPath);
250
250
  const analysis = resolved.document;
251
251
  const sections = externalDocumentSections(virtualPath, analysis);
252
- const resolver = await createResolver(resolved.target.resolvedPath);
252
+ // Remote-authored wiki links may resolve only to configured external files.
253
+ // Do not call the local resolver before rewriting their URLs.
254
+ const available = availableExternalFiles(external, referenceIndex, resolved.target);
255
+ const resolver = async (target) => {
256
+ try {
257
+ const parsed = external.parse(target);
258
+ if (parsed)
259
+ return { href: externalUrl(parsed.identity), referenceCount: 0 };
260
+ }
261
+ catch {
262
+ return null;
263
+ }
264
+ const href = externalDocumentLink(target, resolved.target, external, available);
265
+ return typeof href === 'string' ? { href, referenceCount: 0 } : null;
266
+ };
253
267
  const rendered = analysis.format === 'markdown'
254
268
  ? await renderMarkdown(resolved.fullContent, resolved.target.resolvedPath, resolver, {})
255
269
  : {
@@ -382,10 +396,10 @@ async function readViewSource(latDir, projectRoot, requestedPath, requestedSymbo
382
396
  };
383
397
  }
384
398
  /** Read and highlight a source file after constraining it to the project root. */
385
- export async function getViewSource(latDir, projectRoot, requestedPath, requestedSymbol = '', origin, requestedLine = 0, allSections = [], referenceIndex) {
399
+ export async function getViewSource(latDir, projectRoot, requestedPath, requestedSymbol = '', origin, requestedLine = 0, allSections = [], referenceIndex, publishable) {
386
400
  const source = await readViewSource(latDir, projectRoot, requestedPath, requestedSymbol, requestedLine);
387
401
  const references = referenceIndex
388
- ? await renderSourceReferenceContext(referenceIndex, `${source.path}${requestedSymbol ? `#${requestedSymbol}` : ''}`, origin, latDir, projectRoot, (path) => createMarkdownWikiLinkResolver(latDir, path, allSections, referenceIndex))
402
+ ? await renderSourceReferenceContext(referenceIndex, `${source.path}${requestedSymbol ? `#${requestedSymbol}` : ''}`, origin, latDir, projectRoot, (path) => createMarkdownWikiLinkResolver(latDir, path, allSections, referenceIndex, undefined, publishable))
389
403
  : { context: null, otherReferences: [] };
390
404
  return {
391
405
  ...source,
@@ -0,0 +1,5 @@
1
+ import type { IncomingMessage } from 'node:http';
2
+ /** Check the actual listener authority; forwarding headers are not trusted. */
3
+ export declare function trustedLiveRequest(req: IncomingMessage, configuredHost: string): boolean;
4
+ /** Untrusted repository resources cannot execute with the editor's origin. */
5
+ export declare const RESOURCE_CONTENT_SECURITY_POLICY = "sandbox; default-src 'none'; img-src 'self' data:; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'";
@@ -0,0 +1,46 @@
1
+ const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]']);
2
+ const urlHost = (host) => host.includes(':') && !host.startsWith('[')
3
+ ? `[${host}]`
4
+ : host.toLowerCase();
5
+ /** Check the actual listener authority; forwarding headers are not trusted. */
6
+ export function trustedLiveRequest(req, configuredHost) {
7
+ const authority = req.headers.host;
8
+ if (!authority || !/^[a-z\d.\-:[\]]+$/i.test(authority))
9
+ return false;
10
+ let url;
11
+ try {
12
+ url = new URL(`http://${authority}`);
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ if (Number(url.port || 80) !== req.socket.localPort)
18
+ return false;
19
+ const host = urlHost(configuredHost);
20
+ const allowed = new Set([host]);
21
+ if (LOOPBACK_HOSTS.has(host) || host === '0.0.0.0' || host === '[::]') {
22
+ for (const name of LOOPBACK_HOSTS)
23
+ allowed.add(name);
24
+ if (req.socket.localAddress)
25
+ allowed.add(urlHost(req.socket.localAddress));
26
+ }
27
+ if (!allowed.has(url.hostname))
28
+ return false;
29
+ const origin = req.headers.origin;
30
+ if (origin !== undefined && origin !== url.origin)
31
+ return false;
32
+ if (!['GET', 'HEAD'].includes(req.method ?? 'GET') &&
33
+ req.headers['sec-fetch-site'] === 'cross-site')
34
+ return false;
35
+ // Do not accept an absolute-form URL for a different authority.
36
+ try {
37
+ if (new URL(req.url ?? '/', url).origin !== url.origin)
38
+ return false;
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ return true;
44
+ }
45
+ /** Untrusted repository resources cannot execute with the editor's origin. */
46
+ export const RESOURCE_CONTENT_SECURITY_POLICY = "sandbox; default-src 'none'; img-src 'self' data:; style-src 'unsafe-inline'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'";
@@ -1,4 +1,4 @@
1
- import type { SectionMatch } from '../lattice-model.js';
1
+ import type { SectionMatch } from '@plebeiantech/lat.md-core/lattice-model';
2
2
  import type { ViewSearchResult } from './protocol.js';
3
3
  /** Convert one graph section match into the browser search protocol. */
4
4
  export declare function viewSearchResult(latDir: string, match: SectionMatch, documentPaths?: ReadonlyMap<string, string>): ViewSearchResult;
@@ -1,5 +1,5 @@
1
1
  import { dirname, relative, resolve } from 'node:path';
2
- import { toPosix } from '../path.js';
2
+ import { toPosix } from '@plebeiantech/lat.md-core/path';
3
3
  import { documentUrl } from './document-route.js';
4
4
  /** Convert one graph section match into the browser search protocol. */
5
5
  export function viewSearchResult(latDir, match, documentPaths) {
@@ -1,4 +1,4 @@
1
- import type { Section, SectionMatch } from '../lattice-model.js';
1
+ import type { Section, SectionMatch } from '@plebeiantech/lat.md-core/lattice-model';
2
2
  import type { ViewSearchResponse } from './protocol.js';
3
3
  export type ViewSearch = (query: string) => Promise<ViewSearchResponse>;
4
4
  export type ViewSearchDependencies = {
@@ -1,6 +1,6 @@
1
1
  import { getLatServerVersion } from '../../vendor/lat.md-server/index.js';
2
- import type { CmdContext } from '../context.js';
3
- import { analyzeMarkdownProject, type MarkdownProjectAnalysis } from '../project-analysis.js';
2
+ import type { CmdContext } from '@plebeiantech/lat.md-core/context';
3
+ import { analyzeMarkdownProject, type MarkdownProjectAnalysis } from '@plebeiantech/lat.md-core/project-analysis';
4
4
  import { getLocalVersion } from '../version.js';
5
5
  import { buildStaticView, type StaticViewBuildOptions, type StaticViewBuildResult } from './static-build.js';
6
6
  export type ServerViewBuildOptions = Omit<StaticViewBuildOptions, 'searchApi'>;
@@ -4,9 +4,9 @@ import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
4
4
  import { dirname, join, relative, resolve } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { getLatServerVersion } from '../../vendor/lat.md-server/index.js';
7
- import { analyzeMarkdownProject, } from '../project-analysis.js';
7
+ import { analyzeMarkdownProject, } from '@plebeiantech/lat.md-core/project-analysis';
8
8
  import { getLocalVersion } from '../version.js';
9
- import { toPosix } from '../path.js';
9
+ import { toPosix } from '@plebeiantech/lat.md-core/path';
10
10
  import { buildStaticView, moveViewBuildOutput, removeViewBuildStaging, normalizeStaticViewBasePath, validateViewBuildOutput, } from './static-build.js';
11
11
  import { SERVER_VIEW_SCHEMA_VERSION, } from './server-deployment.js';
12
12
  const defaultDependencies = {
@@ -1,6 +1,6 @@
1
1
  import { type LatServerApp } from '../../vendor/lat.md-server/index.js';
2
2
  import type { Express } from 'express';
3
- import type { Section } from '../lattice-model.js';
3
+ import type { Section } from '@plebeiantech/lat.md-core/lattice-model';
4
4
  import type { CreateSearchEngine } from '../search/embedder.js';
5
5
  import type { ViewSearchResponse } from './protocol.js';
6
6
  export declare const SERVER_VIEW_SCHEMA_VERSION = 1;
@@ -16,7 +16,6 @@ export type ServerViewAppOptions = {
16
16
  app: Express;
17
17
  manifestFile: string | URL;
18
18
  indexFile: string | URL;
19
- cacheDir?: string | URL;
20
19
  createSearchEngine?: CreateSearchEngine;
21
20
  search?: (query: string) => Promise<ViewSearchResponse>;
22
21
  };
@@ -1,7 +1,6 @@
1
1
  import { INDEX_FILE } from '../search/db.js';
2
- import { copyFile, mkdir, mkdtemp, readFile, rm } from 'node:fs/promises';
3
- import { tmpdir } from 'node:os';
4
- import { dirname, join, resolve } from 'node:path';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { basename, dirname, resolve } from 'node:path';
5
4
  import { fileURLToPath } from 'node:url';
6
5
  import { createLatServerApp } from '../../vendor/lat.md-server/index.js';
7
6
  import { createPreindexedViewSearch, } from './preindexed-search.js';
@@ -29,66 +28,27 @@ async function readManifest(path) {
29
28
  }
30
29
  return manifest;
31
30
  }
32
- /** Native SQLite handles may outlive close until the Windows process exits. */
33
- async function removeRuntimeCache(path) {
34
- try {
35
- await rm(path, {
36
- recursive: true,
37
- force: true,
38
- maxRetries: 10,
39
- retryDelay: 150,
40
- });
41
- }
42
- catch (error) {
43
- const code = error.code;
44
- if (process.platform !== 'win32' ||
45
- (code !== 'EBUSY' && code !== 'EPERM' && code !== 'ENOTEMPTY')) {
46
- throw error;
47
- }
48
- // This is an owned OS-temp copy, never the deployed index or user data.
49
- // A lingering native lock must not fail otherwise successful shutdown.
50
- }
51
- }
52
31
  async function prepareServerView(options, manifestFile, indexFile) {
53
32
  const manifest = await readManifest(manifestFile);
54
- let runtimeCacheDir = options.cacheDir ? localPath(options.cacheDir) : '';
55
- let ownsCache = false;
56
33
  let search = options.search;
57
34
  let ownedSearch;
58
35
  if (!search) {
59
- if (!runtimeCacheDir) {
60
- runtimeCacheDir = await mkdtemp(join(tmpdir(), 'lat-ui-search-'));
61
- ownsCache = true;
62
- }
63
- await mkdir(runtimeCacheDir, { recursive: true });
64
- try {
65
- await copyFile(indexFile, join(runtimeCacheDir, INDEX_FILE));
66
- }
67
- catch (error) {
68
- if (ownsCache) {
69
- await removeRuntimeCache(runtimeCacheDir);
70
- }
71
- throw error;
36
+ if (basename(indexFile) !== INDEX_FILE) {
37
+ throw new Error(`Server search index must be named ${INDEX_FILE}: ${indexFile}`);
72
38
  }
73
39
  const documentPaths = new Map();
74
40
  const sections = manifest.sections.map(({ documentPath, ...section }) => {
75
41
  documentPaths.set(section.id.toLowerCase(), documentPath);
76
42
  return { ...section, children: [] };
77
43
  });
78
- ownedSearch = await createPreindexedViewSearch(dirname(indexFile), runtimeCacheDir, sections, documentPaths, undefined, options.createSearchEngine);
44
+ ownedSearch = await createPreindexedViewSearch(dirname(indexFile), dirname(indexFile), sections, documentPaths, undefined, options.createSearchEngine);
79
45
  search = ownedSearch;
80
46
  }
81
47
  return {
82
48
  manifest,
83
49
  search,
84
50
  close: async () => {
85
- try {
86
- await ownedSearch?.close();
87
- }
88
- finally {
89
- if (ownsCache)
90
- await removeRuntimeCache(runtimeCacheDir);
91
- }
51
+ await ownedSearch?.close();
92
52
  },
93
53
  };
94
54
  }
@@ -1,4 +1,4 @@
1
- import type { MarkdownProjectAnalysis } from '../project-analysis.js';
1
+ import type { MarkdownProjectAnalysis } from '@plebeiantech/lat.md-core/project-analysis';
2
2
  export type ServerIndexRequest = {
3
3
  latDir: string;
4
4
  project: MarkdownProjectAnalysis;
@@ -1,6 +1,6 @@
1
1
  import { type Server } from 'node:http';
2
2
  import { type LatServerApp } from '../../vendor/lat.md-server/index.js';
3
- import { type CmdContext } from '../context.js';
3
+ import { type CmdContext } from '@plebeiantech/lat.md-core/context';
4
4
  import { type ViewSearch } from './search.js';
5
5
  import { type ViewStore } from './store.js';
6
6
  export declare const DEFAULT_VIEW_PORT = 4242;
@@ -1,10 +1,11 @@
1
+ import { trustedLiveRequest, RESOURCE_CONTENT_SECURITY_POLICY, } from './request-security.js';
1
2
  import { readFile } from 'node:fs/promises';
2
3
  import { randomUUID } from 'node:crypto';
3
4
  import { extname, join, relative, resolve, sep } from 'node:path';
4
5
  import { fileURLToPath } from 'node:url';
5
6
  import { createLatServerApp, listenLatServer, } from '../../vendor/lat.md-server/index.js';
6
- import { plainStyler } from '../context.js';
7
- import { sectionCommand } from '../cli/section.js';
7
+ import { plainStyler, } from '@plebeiantech/lat.md-core/context';
8
+ import { sectionCommand } from '@plebeiantech/lat.md-core/cli/section';
8
9
  import { DEFAULT_VIEW_LOGO_TEXT, } from './protocol.js';
9
10
  import { ViewDocumentConflictError } from './document-edit.js';
10
11
  import { ViewExternalNotFoundError, ViewDocumentNotFoundError, ViewSourceNotFoundError, } from './repository.js';
@@ -149,9 +150,13 @@ export async function createViewApp(ctx, options = {}) {
149
150
  heartbeat.unref();
150
151
  const handleRequest = (req, res) => {
151
152
  void (async () => {
153
+ if (!trustedLiveRequest(req, host)) {
154
+ send(res, 403, 'text/plain; charset=utf-8', 'Untrusted request origin or host');
155
+ return;
156
+ }
152
157
  const method = req.method ?? 'GET';
153
158
  const headOnly = method === 'HEAD';
154
- const url = new URL(req.url ?? '/', `http://${host}`);
159
+ const url = new URL(req.url ?? '/', `http://${req.headers.host}`);
155
160
  const documentEdit = method === 'PATCH' && url.pathname === '/api/document';
156
161
  if (method !== 'GET' && !headOnly && !documentEdit) {
157
162
  res.setHeader('Allow', 'GET, HEAD');
@@ -358,6 +363,11 @@ export async function createViewApp(ctx, options = {}) {
358
363
  try {
359
364
  const body = await store.getDocumentResource(resourcePath);
360
365
  res.setHeader('Cache-Control', 'no-cache');
366
+ res.setHeader('Content-Security-Policy', RESOURCE_CONTENT_SECURITY_POLICY);
367
+ res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
368
+ if (/\.(?:html?|xhtml|xml|[cm]?js)$/i.test(resourcePath)) {
369
+ res.setHeader('Content-Disposition', 'attachment');
370
+ }
361
371
  send(res, 200, contentType(resourcePath), body, headOnly);
362
372
  }
363
373
  catch (error) {
@@ -1,6 +1,6 @@
1
1
  import { extname } from 'node:path';
2
- import { normalizeRepositoryPath } from '../repository-path.js';
3
- import { isSourceFileExtension } from '../source-formats.js';
2
+ import { normalizeRepositoryPath } from '@plebeiantech/lat.md-core/repository-path';
3
+ import { isSourceFileExtension } from '@plebeiantech/lat.md-core/source-formats';
4
4
  import { rewriteDocumentLink } from './document-route.js';
5
5
  /** Resolve ordinary code links relative to their Markdown file, not the vault root. */
6
6
  export function rewriteLocalFileLink(value, sourcePath) {
@@ -1,5 +1,5 @@
1
1
  import { rename } from 'node:fs/promises';
2
- import type { CmdContext } from '../context.js';
2
+ import type { CmdContext } from '@plebeiantech/lat.md-core/context';
3
3
  export type StaticViewBuildOptions = {
4
4
  basePath?: string;
5
5
  clientDir?: string;
@@ -1,8 +1,9 @@
1
+ import { createPublicationPolicy } from './publication.js';
1
2
  import { createHash } from 'node:crypto';
2
3
  import { cp, lstat, mkdir, mkdtemp, readFile, rename, rm, writeFile, } from 'node:fs/promises';
3
4
  import { dirname, join, parse, relative, resolve, sep } from 'node:path';
4
5
  import { fileURLToPath } from 'node:url';
5
- import { isDocumentPath } from '../document-formats.js';
6
+ import { isDocumentPath } from '@plebeiantech/lat.md-core/document-formats';
6
7
  import { DEFAULT_VIEW_LOGO_TEXT } from './protocol.js';
7
8
  import { documentTreeUrls, rewriteDocumentTreeUrls } from './document-tree.js';
8
9
  import { VIEW_STATIC_BOOTSTRAP_ID, viewStaticDocumentRequest, viewStaticSourceKey, } from './static-protocol.js';
@@ -182,8 +183,14 @@ function positiveInteger(value) {
182
183
  return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
183
184
  }
184
185
  function sourceRequest(value) {
185
- const url = new URL(decodeHtmlUrlAttribute(value), 'http://lat.local');
186
- if (!url.pathname.startsWith('/code/'))
186
+ let url;
187
+ try {
188
+ url = new URL(decodeHtmlUrlAttribute(value), 'http://lat.local');
189
+ }
190
+ catch {
191
+ return null;
192
+ }
193
+ if (url.origin !== 'http://lat.local' || !url.pathname.startsWith('/code/'))
187
194
  return null;
188
195
  let path;
189
196
  let symbol;
@@ -193,6 +200,14 @@ function sourceRequest(value) {
193
200
  .split('/')
194
201
  .map(decodeURIComponent)
195
202
  .join('/');
203
+ if (path.split('/').some((part) => !part || part === '.' || part === '..') ||
204
+ path.includes('\\') ||
205
+ path.includes('\0') ||
206
+ /^[a-z]:/i.test(path))
207
+ return null;
208
+ // Encoded separators cannot create extra route components after decoding.
209
+ if (/%(?:2f|5c)/i.test(url.pathname))
210
+ return null;
196
211
  symbol = decodeURIComponent(url.hash.slice(1));
197
212
  }
198
213
  catch {
@@ -214,7 +229,8 @@ function externalRequest(value, external) {
214
229
  catch {
215
230
  return null;
216
231
  }
217
- if (!url.pathname.startsWith('/external/'))
232
+ if (url.origin !== 'http://lat.local' ||
233
+ !url.pathname.startsWith('/external/'))
218
234
  return null;
219
235
  try {
220
236
  const parts = url.pathname
@@ -441,8 +457,24 @@ function redirectShell(target, basePath) {
441
457
  </html>
442
458
  `;
443
459
  }
460
+ /** All authored route writes must remain strictly below the payload root. */
461
+ function publicationOutputPath(root, ...parts) {
462
+ if (parts.some((part) => part.includes('\\') ||
463
+ part.includes('\0') ||
464
+ part.split('/').some((segment) => segment === '..' || segment === '.')))
465
+ throw new Error('Unsafe export route');
466
+ const path = resolve(root, ...parts);
467
+ const rel = relative(root, path);
468
+ if (!rel ||
469
+ rel.startsWith(`..${sep}`) ||
470
+ rel === '..' ||
471
+ /^[a-z]:/i.test(rel) ||
472
+ rel.startsWith(sep))
473
+ throw new Error('Export route is outside the output directory');
474
+ return path;
475
+ }
444
476
  async function writeRouteShell(outputDir, route, shell) {
445
- const path = join(outputDir, ...route.split('/'), 'index.html');
477
+ const path = publicationOutputPath(outputDir, route, 'index.html');
446
478
  await mkdir(dirname(path), { recursive: true });
447
479
  await writeFile(path, shell);
448
480
  }
@@ -455,14 +487,16 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
455
487
  await validateViewBuildOutput(outputDir, ctx.projectRoot, 'Static', options.force);
456
488
  await mkdir(dirname(outputDir), { recursive: true });
457
489
  const stagingDir = await mkdtemp(join(dirname(outputDir), '.lat-static-staging-'));
458
- const store = await createViewStore(ctx.latDir, ctx.projectRoot, {
459
- codeExcludePaths: [outputDir, ...(options.codeExcludePaths ?? [])],
460
- git: false,
461
- watch: false,
462
- externalIgnoreLocal: true,
463
- externalCa: options.externalCa,
464
- });
490
+ let store;
465
491
  try {
492
+ store = await createViewStore(ctx.latDir, ctx.projectRoot, {
493
+ codeExcludePaths: [outputDir, ...(options.codeExcludePaths ?? [])],
494
+ git: false,
495
+ watch: false,
496
+ externalIgnoreLocal: true,
497
+ externalCa: options.externalCa,
498
+ publishable: await createPublicationPolicy(ctx.projectRoot),
499
+ });
466
500
  const clientHtml = await readFile(join(clientDir, 'index.html'), 'utf8');
467
501
  const payloadDir = staticViewPayloadDir(stagingDir, basePath);
468
502
  await mkdir(payloadDir, { recursive: true });
@@ -523,11 +557,9 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
523
557
  externals.set(target, external);
524
558
  const before = externalRequests.size;
525
559
  if (external.kind === 'markdown') {
526
- sourceRequestsFromDocument(external.document, sourceRequests);
527
560
  externalRequestsFromDocument(external.document, externalRequests, store.snapshot.external, external.document.path);
528
561
  }
529
562
  else {
530
- sourceRequestsFromSource(external.source, sourceRequests);
531
563
  externalRequestsFromSource(external.source, externalRequests, store.snapshot.external);
532
564
  }
533
565
  if (externalRequests.size > before) {
@@ -580,7 +612,7 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
580
612
  const rewritten = rewriteDocument(document, basePath, index.entry, sectionPaths, documentPaths);
581
613
  manifest.documents[path] = await writeDataJson(payloadDir, 'documents', rewritten);
582
614
  const source = await store.getDocumentSource(path);
583
- const rawPath = join(payloadDir, ...path.split('/'));
615
+ const rawPath = publicationOutputPath(payloadDir, path);
584
616
  await mkdir(dirname(rawPath), { recursive: true });
585
617
  await writeFile(rawPath, source.content);
586
618
  const route = path.slice(0, -'.md'.length);
@@ -597,7 +629,7 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
597
629
  catch (error) {
598
630
  throw new Error(`Could not export document resource ${path}: ${error.message}`);
599
631
  }
600
- const outputPath = join(payloadDir, 'resources', ...path.split('/'));
632
+ const outputPath = publicationOutputPath(payloadDir, 'resources', path);
601
633
  await mkdir(dirname(outputPath), { recursive: true });
602
634
  await writeFile(outputPath, content);
603
635
  }
@@ -704,6 +736,6 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
704
736
  throw error;
705
737
  }
706
738
  finally {
707
- await store.close();
739
+ await store?.close();
708
740
  }
709
741
  }
@@ -1,5 +1,5 @@
1
- import { type ExternalResolver } from '../external-sources.js';
2
- import type { Section } from '../lattice-model.js';
1
+ import { type ExternalResolver } from '@plebeiantech/lat.md-core/external-sources';
2
+ import type { Section } from '@plebeiantech/lat.md-core/lattice-model';
3
3
  import { type ViewGitRepository, type ViewGitSnapshot } from './git.js';
4
4
  import type { ViewDocument, ViewDocumentEditResponse, ViewDocumentError, ViewDocumentSource, ViewExternalDocument, ViewGraph, ViewIndex, ViewProjectGeneration, ViewDocumentTree, ViewSourceDocument } from './protocol.js';
5
5
  import { type ViewCodeReferenceFile, type ViewParsedMarkdownFile, type ViewReferenceIndex } from './references.js';
@@ -16,6 +16,8 @@ export type ViewProjectSnapshot = {
16
16
  external: ExternalResolver;
17
17
  };
18
18
  export type ViewStoreOptions = {
19
+ /** Export-only allowlist, applied before source/backlink/resource reads. */
20
+ publishable?: (path: string) => Promise<boolean>;
19
21
  codeExcludePaths?: string[];
20
22
  debounceMs?: number;
21
23
  git?: boolean;