canopycms 0.0.63 → 0.0.64

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 (1209) hide show
  1. package/README.md +153 -19
  2. package/dist/ai/generate.d.ts +4 -5
  3. package/dist/ai/generate.js +0 -1
  4. package/dist/ai/handler.d.ts +3 -4
  5. package/dist/ai/handler.js +0 -1
  6. package/dist/ai/index.d.ts +7 -8
  7. package/dist/ai/index.js +0 -1
  8. package/dist/ai/json-to-markdown.d.ts +1 -2
  9. package/dist/ai/json-to-markdown.js +11 -2
  10. package/dist/ai/resolve-branch.d.ts +1 -2
  11. package/dist/ai/resolve-branch.js +0 -1
  12. package/dist/ai/strip-mdx.d.ts +0 -1
  13. package/dist/ai/strip-mdx.js +0 -1
  14. package/dist/ai/to-plain-text.d.ts +4 -1
  15. package/dist/ai/to-plain-text.js +47 -1
  16. package/dist/ai/transform-components.d.ts +1 -2
  17. package/dist/ai/transform-components.js +0 -1
  18. package/dist/ai/types.d.ts +1 -2
  19. package/dist/ai/types.js +0 -1
  20. package/dist/api/admin-branch-health.d.ts +7 -8
  21. package/dist/api/admin-branch-health.js +0 -1
  22. package/dist/api/admin.d.ts +16 -17
  23. package/dist/api/admin.js +0 -1
  24. package/dist/api/assets.d.ts +8 -9
  25. package/dist/api/assets.js +0 -1
  26. package/dist/api/branch-merge.d.ts +3 -4
  27. package/dist/api/branch-merge.js +0 -1
  28. package/dist/api/branch-review.d.ts +5 -6
  29. package/dist/api/branch-review.js +0 -1
  30. package/dist/api/branch-status.d.ts +15 -16
  31. package/dist/api/branch-status.js +0 -1
  32. package/dist/api/branch-withdraw.d.ts +3 -4
  33. package/dist/api/branch-withdraw.js +0 -1
  34. package/dist/api/branch.d.ts +13 -14
  35. package/dist/api/branch.js +0 -1
  36. package/dist/api/client.d.ts +14 -15
  37. package/dist/api/client.js +0 -1
  38. package/dist/api/comments.d.ts +11 -12
  39. package/dist/api/comments.js +0 -1
  40. package/dist/api/content.d.ts +22 -24
  41. package/dist/api/content.js +132 -18
  42. package/dist/api/entries-constants.d.ts +0 -1
  43. package/dist/api/entries-constants.js +0 -1
  44. package/dist/api/entries.d.ts +12 -13
  45. package/dist/api/entries.js +12 -2
  46. package/dist/api/github-sync.d.ts +2 -3
  47. package/dist/api/github-sync.js +0 -1
  48. package/dist/api/groups.d.ts +6 -7
  49. package/dist/api/groups.js +0 -1
  50. package/dist/api/guards.d.ts +2 -3
  51. package/dist/api/guards.js +0 -1
  52. package/dist/api/index.d.ts +18 -19
  53. package/dist/api/index.js +0 -1
  54. package/dist/api/permissions.d.ts +10 -11
  55. package/dist/api/permissions.js +0 -1
  56. package/dist/api/reference-options.d.ts +3 -4
  57. package/dist/api/reference-options.js +0 -1
  58. package/dist/api/request-body-hash.d.ts +0 -1
  59. package/dist/api/request-body-hash.js +0 -1
  60. package/dist/api/resolve-references.d.ts +6 -7
  61. package/dist/api/resolve-references.js +24 -6
  62. package/dist/api/route-builder.d.ts +2 -3
  63. package/dist/api/route-builder.js +0 -1
  64. package/dist/api/schema.d.ts +43 -44
  65. package/dist/api/schema.js +0 -1
  66. package/dist/api/settings-helpers.d.ts +2 -3
  67. package/dist/api/settings-helpers.js +0 -1
  68. package/dist/api/types.d.ts +6 -7
  69. package/dist/api/types.js +0 -1
  70. package/dist/api/user.d.ts +2 -3
  71. package/dist/api/user.js +0 -1
  72. package/dist/api/validators.d.ts +3 -4
  73. package/dist/api/validators.js +0 -1
  74. package/dist/assets/asset-prefixes.d.ts +1 -2
  75. package/dist/assets/asset-prefixes.js +0 -1
  76. package/dist/assets/asset-src.d.ts +17 -5
  77. package/dist/assets/asset-src.js +15 -3
  78. package/dist/assets/asset-url.d.ts +28 -7
  79. package/dist/assets/asset-url.js +20 -15
  80. package/dist/assets/factory.d.ts +2 -3
  81. package/dist/assets/factory.js +0 -2
  82. package/dist/assets/finalize.d.ts +2 -3
  83. package/dist/assets/finalize.js +0 -1
  84. package/dist/assets/index.d.ts +12 -13
  85. package/dist/assets/index.js +0 -1
  86. package/dist/assets/keys.d.ts +1 -2
  87. package/dist/assets/keys.js +0 -1
  88. package/dist/assets/pipeline.d.ts +1 -2
  89. package/dist/assets/pipeline.js +0 -1
  90. package/dist/assets/store-local.d.ts +1 -2
  91. package/dist/assets/store-local.js +0 -1
  92. package/dist/assets/store-s3.d.ts +2 -4
  93. package/dist/assets/store-s3.js +0 -1
  94. package/dist/assets/svg-sanitizer.d.ts +0 -1
  95. package/dist/assets/svg-sanitizer.js +0 -1
  96. package/dist/assets/transform-directives.d.ts +0 -1
  97. package/dist/assets/transform-directives.js +0 -1
  98. package/dist/assets/transform.d.ts +1 -2
  99. package/dist/assets/transform.js +0 -1
  100. package/dist/assets/types.d.ts +0 -1
  101. package/dist/assets/types.js +0 -1
  102. package/dist/auth/cache.d.ts +2 -3
  103. package/dist/auth/cache.js +0 -1
  104. package/dist/auth/caching-auth-plugin.d.ts +3 -4
  105. package/dist/auth/caching-auth-plugin.js +0 -1
  106. package/dist/auth/context-helpers.d.ts +1 -2
  107. package/dist/auth/context-helpers.js +0 -1
  108. package/dist/auth/file-based-auth-cache.d.ts +3 -4
  109. package/dist/auth/file-based-auth-cache.js +0 -1
  110. package/dist/auth/index.d.ts +6 -7
  111. package/dist/auth/index.js +0 -1
  112. package/dist/auth/plugin.d.ts +3 -4
  113. package/dist/auth/plugin.js +0 -1
  114. package/dist/auth/types.d.ts +1 -2
  115. package/dist/auth/types.js +0 -1
  116. package/dist/authorization/branch.d.ts +4 -5
  117. package/dist/authorization/branch.js +0 -1
  118. package/dist/authorization/content.d.ts +6 -7
  119. package/dist/authorization/content.js +0 -1
  120. package/dist/authorization/groups/index.d.ts +2 -3
  121. package/dist/authorization/groups/index.js +0 -1
  122. package/dist/authorization/groups/loader.d.ts +3 -4
  123. package/dist/authorization/groups/loader.js +0 -1
  124. package/dist/authorization/groups/schema.d.ts +1 -2
  125. package/dist/authorization/groups/schema.js +0 -1
  126. package/dist/authorization/helpers.d.ts +0 -1
  127. package/dist/authorization/helpers.js +0 -1
  128. package/dist/authorization/index.d.ts +11 -12
  129. package/dist/authorization/index.js +0 -1
  130. package/dist/authorization/path.d.ts +4 -5
  131. package/dist/authorization/path.js +0 -1
  132. package/dist/authorization/permissions/index.d.ts +2 -3
  133. package/dist/authorization/permissions/index.js +0 -1
  134. package/dist/authorization/permissions/loader.d.ts +4 -5
  135. package/dist/authorization/permissions/loader.js +0 -1
  136. package/dist/authorization/permissions/schema.d.ts +2 -3
  137. package/dist/authorization/permissions/schema.js +0 -1
  138. package/dist/authorization/protected-branch.d.ts +2 -3
  139. package/dist/authorization/protected-branch.js +0 -1
  140. package/dist/authorization/settings-file-store.d.ts +1 -2
  141. package/dist/authorization/settings-file-store.js +0 -1
  142. package/dist/authorization/types.d.ts +6 -7
  143. package/dist/authorization/types.js +0 -1
  144. package/dist/authorization/validation.d.ts +1 -2
  145. package/dist/authorization/validation.js +0 -1
  146. package/dist/branch-health.d.ts +2 -3
  147. package/dist/branch-health.js +0 -1
  148. package/dist/branch-metadata.d.ts +2 -3
  149. package/dist/branch-metadata.js +0 -1
  150. package/dist/branch-registry.d.ts +1 -2
  151. package/dist/branch-registry.js +0 -1
  152. package/dist/branch-schema-cache.d.ts +4 -5
  153. package/dist/branch-schema-cache.js +0 -1
  154. package/dist/branch-workspace.d.ts +4 -5
  155. package/dist/branch-workspace.js +0 -1
  156. package/dist/build/generate-ai-content.d.ts +21 -5
  157. package/dist/build/generate-ai-content.js +98 -6
  158. package/dist/build/index.d.ts +2 -3
  159. package/dist/build/index.js +0 -1
  160. package/dist/build-canopy.d.ts +5 -6
  161. package/dist/build-canopy.js +0 -1
  162. package/dist/build-mode.d.ts +1 -2
  163. package/dist/build-mode.js +0 -1
  164. package/dist/cli/cli.d.ts +1 -2
  165. package/dist/cli/cli.js +1131 -486
  166. package/dist/cli/generate-ai-content.d.ts +0 -1
  167. package/dist/cli/generate-ai-content.js +1000 -360
  168. package/dist/cli/init.d.ts +1 -2
  169. package/dist/cli/init.js +12 -2
  170. package/dist/cli/migrate.d.ts +5 -3
  171. package/dist/cli/migrate.js +5 -3
  172. package/dist/cli/project-detect.d.ts +5 -2
  173. package/dist/cli/project-detect.js +5 -1
  174. package/dist/cli/project-root.d.ts +0 -1
  175. package/dist/cli/project-root.js +0 -1
  176. package/dist/cli/sync.d.ts +0 -1
  177. package/dist/cli/sync.js +0 -1
  178. package/dist/cli/template-files/deploy-cms.yml.template +8 -6
  179. package/dist/cli/templates.d.ts +0 -1
  180. package/dist/cli/templates.js +0 -1
  181. package/dist/client.d.ts +13 -14
  182. package/dist/client.js +0 -1
  183. package/dist/comment-store.d.ts +0 -1
  184. package/dist/comment-store.js +0 -1
  185. package/dist/config/flatten.d.ts +1 -2
  186. package/dist/config/flatten.js +0 -1
  187. package/dist/config/helpers.d.ts +3 -4
  188. package/dist/config/helpers.js +2 -2
  189. package/dist/config/index.d.ts +10 -11
  190. package/dist/config/index.js +0 -1
  191. package/dist/config/schemas/collection.d.ts +0 -1
  192. package/dist/config/schemas/collection.js +0 -1
  193. package/dist/config/schemas/config.d.ts +7 -4
  194. package/dist/config/schemas/config.js +12 -1
  195. package/dist/config/schemas/field.d.ts +3 -1
  196. package/dist/config/schemas/field.js +5 -1
  197. package/dist/config/schemas/media.d.ts +0 -1
  198. package/dist/config/schemas/media.js +0 -1
  199. package/dist/config/schemas/permissions.d.ts +0 -1
  200. package/dist/config/schemas/permissions.js +0 -1
  201. package/dist/config/types.d.ts +55 -9
  202. package/dist/config/types.js +0 -1
  203. package/dist/config/validation.d.ts +1 -2
  204. package/dist/config/validation.js +0 -1
  205. package/dist/config.d.ts +2 -3
  206. package/dist/config.js +0 -1
  207. package/dist/content-id-index.d.ts +1 -2
  208. package/dist/content-id-index.js +0 -1
  209. package/dist/content-index-generation.d.ts +0 -1
  210. package/dist/content-index-generation.js +0 -1
  211. package/dist/content-index-registry.d.ts +0 -1
  212. package/dist/content-index-registry.js +0 -1
  213. package/dist/content-listing.d.ts +75 -59
  214. package/dist/content-listing.js +76 -83
  215. package/dist/content-reader.d.ts +4 -5
  216. package/dist/content-reader.js +22 -3
  217. package/dist/content-store.d.ts +137 -6
  218. package/dist/content-store.js +294 -22
  219. package/dist/content-tree.d.ts +15 -5
  220. package/dist/content-tree.js +27 -8
  221. package/dist/context.d.ts +9 -6
  222. package/dist/context.js +0 -1
  223. package/dist/dev-content-watcher.d.ts +2 -3
  224. package/dist/dev-content-watcher.js +0 -1
  225. package/dist/editor/BranchManager.d.ts +4 -5
  226. package/dist/editor/BranchManager.js +0 -1
  227. package/dist/editor/CanopyEditor.d.ts +2 -3
  228. package/dist/editor/CanopyEditor.js +1 -2
  229. package/dist/editor/CanopyEditorPage.d.ts +2 -3
  230. package/dist/editor/CanopyEditorPage.js +0 -1
  231. package/dist/editor/CommentsPanel.d.ts +2 -3
  232. package/dist/editor/CommentsPanel.js +0 -1
  233. package/dist/editor/Editor.d.ts +13 -8
  234. package/dist/editor/Editor.js +4 -5
  235. package/dist/editor/EditorPanes.d.ts +0 -1
  236. package/dist/editor/EditorPanes.js +0 -1
  237. package/dist/editor/EntryNavigator.d.ts +1 -2
  238. package/dist/editor/EntryNavigator.js +0 -1
  239. package/dist/editor/FormRenderer.d.ts +2 -3
  240. package/dist/editor/FormRenderer.js +22 -3
  241. package/dist/editor/GroupManager.d.ts +2 -3
  242. package/dist/editor/GroupManager.js +0 -1
  243. package/dist/editor/PermissionManager.d.ts +2 -3
  244. package/dist/editor/PermissionManager.js +0 -1
  245. package/dist/editor/admin/SystemHealthPanel.d.ts +0 -1
  246. package/dist/editor/admin/SystemHealthPanel.js +0 -1
  247. package/dist/editor/admin/useSystemHealth.d.ts +2 -3
  248. package/dist/editor/admin/useSystemHealth.js +0 -1
  249. package/dist/editor/canopy-path.d.ts +0 -1
  250. package/dist/editor/canopy-path.js +0 -1
  251. package/dist/editor/client-reference-resolver.d.ts +13 -4
  252. package/dist/editor/client-reference-resolver.js +18 -8
  253. package/dist/editor/comments/BranchComments.d.ts +2 -3
  254. package/dist/editor/comments/BranchComments.js +0 -1
  255. package/dist/editor/comments/EntryComments.d.ts +1 -2
  256. package/dist/editor/comments/EntryComments.js +0 -1
  257. package/dist/editor/comments/FieldWrapper.d.ts +1 -2
  258. package/dist/editor/comments/FieldWrapper.js +0 -1
  259. package/dist/editor/comments/InlineCommentThread.d.ts +2 -3
  260. package/dist/editor/comments/InlineCommentThread.js +0 -1
  261. package/dist/editor/comments/ThreadCarousel.d.ts +2 -3
  262. package/dist/editor/comments/ThreadCarousel.js +0 -1
  263. package/dist/editor/components/ConfirmDeleteModal.d.ts +0 -1
  264. package/dist/editor/components/ConfirmDeleteModal.js +0 -1
  265. package/dist/editor/components/EditorFooter.d.ts +0 -1
  266. package/dist/editor/components/EditorFooter.js +0 -1
  267. package/dist/editor/components/EditorHeader.d.ts +4 -5
  268. package/dist/editor/components/EditorHeader.js +0 -1
  269. package/dist/editor/components/EditorSidebar.d.ts +1 -2
  270. package/dist/editor/components/EditorSidebar.js +0 -1
  271. package/dist/editor/components/EntryCreateModal.d.ts +0 -1
  272. package/dist/editor/components/EntryCreateModal.js +21 -10
  273. package/dist/editor/components/RenameEntryModal.d.ts +0 -1
  274. package/dist/editor/components/RenameEntryModal.js +0 -1
  275. package/dist/editor/components/UserBadge.d.ts +2 -3
  276. package/dist/editor/components/UserBadge.js +0 -1
  277. package/dist/editor/components/index.d.ts +3 -4
  278. package/dist/editor/components/index.js +0 -1
  279. package/dist/editor/context/ApiClientContext.d.ts +9 -3
  280. package/dist/editor/context/ApiClientContext.js +8 -4
  281. package/dist/editor/context/AssetContext.d.ts +26 -2
  282. package/dist/editor/context/AssetContext.js +23 -3
  283. package/dist/editor/context/EditorStateContext.d.ts +1 -2
  284. package/dist/editor/context/EditorStateContext.js +0 -1
  285. package/dist/editor/context/SWRProvider.d.ts +0 -1
  286. package/dist/editor/context/SWRProvider.js +0 -1
  287. package/dist/editor/context/index.d.ts +4 -5
  288. package/dist/editor/context/index.js +0 -1
  289. package/dist/editor/editor-config.d.ts +2 -3
  290. package/dist/editor/editor-config.js +0 -1
  291. package/dist/editor/editor-utils.d.ts +22 -10
  292. package/dist/editor/editor-utils.js +41 -10
  293. package/dist/editor/fields/BlockField.d.ts +1 -2
  294. package/dist/editor/fields/BlockField.js +0 -1
  295. package/dist/editor/fields/CodeField.d.ts +0 -1
  296. package/dist/editor/fields/CodeField.js +0 -1
  297. package/dist/editor/fields/DateTimeField.d.ts +0 -1
  298. package/dist/editor/fields/DateTimeField.js +0 -1
  299. package/dist/editor/fields/ImageField.d.ts +1 -2
  300. package/dist/editor/fields/ImageField.js +0 -1
  301. package/dist/editor/fields/InlineGroupField.d.ts +2 -3
  302. package/dist/editor/fields/InlineGroupField.js +0 -1
  303. package/dist/editor/fields/MarkdownField.d.ts +0 -1
  304. package/dist/editor/fields/MarkdownField.js +0 -1
  305. package/dist/editor/fields/MdxImageDialog.d.ts +0 -1
  306. package/dist/editor/fields/MdxImageDialog.js +0 -1
  307. package/dist/editor/fields/NumberField.d.ts +0 -1
  308. package/dist/editor/fields/NumberField.js +0 -1
  309. package/dist/editor/fields/NumberListField.d.ts +0 -1
  310. package/dist/editor/fields/NumberListField.js +0 -1
  311. package/dist/editor/fields/ObjectField.d.ts +11 -2
  312. package/dist/editor/fields/ObjectField.js +3 -4
  313. package/dist/editor/fields/ReferenceField.d.ts +0 -1
  314. package/dist/editor/fields/ReferenceField.js +8 -3
  315. package/dist/editor/fields/SelectField.d.ts +0 -1
  316. package/dist/editor/fields/SelectField.js +0 -1
  317. package/dist/editor/fields/StringListField.d.ts +0 -1
  318. package/dist/editor/fields/StringListField.js +0 -1
  319. package/dist/editor/fields/TextField.d.ts +0 -1
  320. package/dist/editor/fields/TextField.js +0 -1
  321. package/dist/editor/fields/ToggleField.d.ts +0 -1
  322. package/dist/editor/fields/ToggleField.js +0 -1
  323. package/dist/editor/fields/entry-link/EntryLinkContext.d.ts +1 -2
  324. package/dist/editor/fields/entry-link/EntryLinkContext.js +0 -1
  325. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +0 -1
  326. package/dist/editor/fields/entry-link/InsertEntryLink.js +0 -1
  327. package/dist/editor/fields/entry-link/index.d.ts +2 -3
  328. package/dist/editor/fields/entry-link/index.js +0 -1
  329. package/dist/editor/group-manager/ExternalGroupsTab.d.ts +1 -2
  330. package/dist/editor/group-manager/ExternalGroupsTab.js +0 -1
  331. package/dist/editor/group-manager/GroupCard.d.ts +1 -2
  332. package/dist/editor/group-manager/GroupCard.js +0 -1
  333. package/dist/editor/group-manager/GroupForm.d.ts +1 -2
  334. package/dist/editor/group-manager/GroupForm.js +0 -1
  335. package/dist/editor/group-manager/InternalGroupsTab.d.ts +1 -2
  336. package/dist/editor/group-manager/InternalGroupsTab.js +0 -1
  337. package/dist/editor/group-manager/MemberList.d.ts +1 -2
  338. package/dist/editor/group-manager/MemberList.js +0 -1
  339. package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts +1 -2
  340. package/dist/editor/group-manager/hooks/useExternalGroupSearch.js +0 -1
  341. package/dist/editor/group-manager/hooks/useGroupState.d.ts +1 -2
  342. package/dist/editor/group-manager/hooks/useGroupState.js +0 -1
  343. package/dist/editor/group-manager/hooks/useUserSearch.d.ts +1 -2
  344. package/dist/editor/group-manager/hooks/useUserSearch.js +0 -1
  345. package/dist/editor/group-manager/index.d.ts +5 -6
  346. package/dist/editor/group-manager/index.js +0 -1
  347. package/dist/editor/group-manager/types.d.ts +4 -5
  348. package/dist/editor/group-manager/types.js +0 -1
  349. package/dist/editor/hooks/index.d.ts +14 -15
  350. package/dist/editor/hooks/index.js +0 -1
  351. package/dist/editor/hooks/useBranchActions.d.ts +0 -1
  352. package/dist/editor/hooks/useBranchActions.js +0 -1
  353. package/dist/editor/hooks/useBranchManager.d.ts +4 -5
  354. package/dist/editor/hooks/useBranchManager.js +0 -1
  355. package/dist/editor/hooks/useBranchesData.d.ts +2 -3
  356. package/dist/editor/hooks/useBranchesData.js +0 -1
  357. package/dist/editor/hooks/useCommentSystem.d.ts +2 -3
  358. package/dist/editor/hooks/useCommentSystem.js +0 -1
  359. package/dist/editor/hooks/useCommentsData.d.ts +2 -3
  360. package/dist/editor/hooks/useCommentsData.js +0 -1
  361. package/dist/editor/hooks/useDraftManager.d.ts +3 -4
  362. package/dist/editor/hooks/useDraftManager.js +0 -1
  363. package/dist/editor/hooks/useEditorLayout.d.ts +1 -2
  364. package/dist/editor/hooks/useEditorLayout.js +0 -1
  365. package/dist/editor/hooks/useEntriesData.d.ts +3 -5
  366. package/dist/editor/hooks/useEntriesData.js +0 -3
  367. package/dist/editor/hooks/useEntryLinkResolution.d.ts +2 -3
  368. package/dist/editor/hooks/useEntryLinkResolution.js +0 -1
  369. package/dist/editor/hooks/useEntryManager.d.ts +5 -7
  370. package/dist/editor/hooks/useEntryManager.js +8 -3
  371. package/dist/editor/hooks/useGroupManager.d.ts +2 -3
  372. package/dist/editor/hooks/useGroupManager.js +0 -1
  373. package/dist/editor/hooks/usePermissionManager.d.ts +2 -3
  374. package/dist/editor/hooks/usePermissionManager.js +0 -1
  375. package/dist/editor/hooks/useReferenceResolution.d.ts +1 -2
  376. package/dist/editor/hooks/useReferenceResolution.js +8 -3
  377. package/dist/editor/hooks/useSchemaManager.d.ts +2 -3
  378. package/dist/editor/hooks/useSchemaManager.js +0 -1
  379. package/dist/editor/hooks/useUserContext.d.ts +1 -2
  380. package/dist/editor/hooks/useUserContext.js +8 -3
  381. package/dist/editor/hooks/useUserMetadata.d.ts +2 -3
  382. package/dist/editor/hooks/useUserMetadata.js +0 -1
  383. package/dist/editor/media/AssetCard.d.ts +1 -2
  384. package/dist/editor/media/AssetCard.js +0 -1
  385. package/dist/editor/media/CropStep.d.ts +1 -2
  386. package/dist/editor/media/CropStep.js +0 -1
  387. package/dist/editor/media/MediaLibrary.d.ts +1 -2
  388. package/dist/editor/media/MediaLibrary.js +0 -1
  389. package/dist/editor/media/MediaLibraryBody.d.ts +1 -2
  390. package/dist/editor/media/MediaLibraryBody.js +0 -1
  391. package/dist/editor/media/crop-math.d.ts +1 -2
  392. package/dist/editor/media/crop-math.js +0 -1
  393. package/dist/editor/media/upload-asset.d.ts +2 -3
  394. package/dist/editor/media/upload-asset.js +0 -1
  395. package/dist/editor/media/upload-constants.d.ts +0 -1
  396. package/dist/editor/media/upload-constants.js +0 -1
  397. package/dist/editor/media/useAssetUpload.d.ts +1 -2
  398. package/dist/editor/media/useAssetUpload.js +0 -1
  399. package/dist/editor/media/xhr-upload.d.ts +0 -1
  400. package/dist/editor/media/xhr-upload.js +0 -1
  401. package/dist/editor/permission-manager/GroupSelector.d.ts +1 -2
  402. package/dist/editor/permission-manager/GroupSelector.js +0 -1
  403. package/dist/editor/permission-manager/PermissionEditor.d.ts +1 -2
  404. package/dist/editor/permission-manager/PermissionEditor.js +0 -1
  405. package/dist/editor/permission-manager/PermissionLevelBadge.d.ts +1 -2
  406. package/dist/editor/permission-manager/PermissionLevelBadge.js +0 -1
  407. package/dist/editor/permission-manager/PermissionTree.d.ts +1 -2
  408. package/dist/editor/permission-manager/PermissionTree.js +0 -1
  409. package/dist/editor/permission-manager/UserSelector.d.ts +1 -2
  410. package/dist/editor/permission-manager/UserSelector.js +0 -1
  411. package/dist/editor/permission-manager/constants.d.ts +1 -2
  412. package/dist/editor/permission-manager/constants.js +0 -1
  413. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +1 -2
  414. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +0 -1
  415. package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts +2 -3
  416. package/dist/editor/permission-manager/hooks/usePermissionTree.js +0 -1
  417. package/dist/editor/permission-manager/index.d.ts +4 -5
  418. package/dist/editor/permission-manager/index.js +0 -1
  419. package/dist/editor/permission-manager/types.d.ts +3 -4
  420. package/dist/editor/permission-manager/types.js +0 -1
  421. package/dist/editor/permission-manager/utils.d.ts +2 -3
  422. package/dist/editor/permission-manager/utils.js +0 -1
  423. package/dist/editor/preview-bridge.d.ts +1 -2
  424. package/dist/editor/preview-bridge.js +0 -1
  425. package/dist/editor/relative-time.d.ts +0 -1
  426. package/dist/editor/relative-time.js +0 -1
  427. package/dist/editor/schema-editor/CollectionEditor.d.ts +4 -5
  428. package/dist/editor/schema-editor/CollectionEditor.js +0 -1
  429. package/dist/editor/schema-editor/EntryTypeEditor.d.ts +2 -3
  430. package/dist/editor/schema-editor/EntryTypeEditor.js +0 -1
  431. package/dist/editor/schema-editor/index.d.ts +2 -3
  432. package/dist/editor/schema-editor/index.js +0 -1
  433. package/dist/editor/theme.d.ts +0 -1
  434. package/dist/editor/theme.js +0 -1
  435. package/dist/editor/utils/env.d.ts +0 -1
  436. package/dist/editor/utils/env.js +0 -1
  437. package/dist/entry-link-resolver.d.ts +1 -2
  438. package/dist/entry-link-resolver.js +0 -1
  439. package/dist/entry-schema-registry.d.ts +3 -4
  440. package/dist/entry-schema-registry.js +6 -2
  441. package/dist/entry-schema.d.ts +114 -3
  442. package/dist/entry-schema.js +46 -1
  443. package/dist/git-manager.d.ts +1 -2
  444. package/dist/git-manager.js +0 -1
  445. package/dist/github-service.d.ts +1 -2
  446. package/dist/github-service.js +0 -1
  447. package/dist/http/handler.d.ts +6 -7
  448. package/dist/http/handler.js +0 -1
  449. package/dist/http/index.d.ts +6 -7
  450. package/dist/http/index.js +0 -1
  451. package/dist/http/router.d.ts +2 -3
  452. package/dist/http/router.js +0 -1
  453. package/dist/http/types.d.ts +0 -1
  454. package/dist/http/types.js +0 -1
  455. package/dist/id.d.ts +2 -3
  456. package/dist/id.js +0 -1
  457. package/dist/index.d.ts +17 -18
  458. package/dist/index.js +0 -1
  459. package/dist/operating-mode/client-safe-strategy.d.ts +1 -2
  460. package/dist/operating-mode/client-safe-strategy.js +0 -1
  461. package/dist/operating-mode/client-unsafe-strategy.d.ts +1 -2
  462. package/dist/operating-mode/client-unsafe-strategy.js +0 -1
  463. package/dist/operating-mode/client.d.ts +3 -4
  464. package/dist/operating-mode/client.js +0 -1
  465. package/dist/operating-mode/deployment-name-fixtures.d.ts +0 -1
  466. package/dist/operating-mode/deployment-name-fixtures.js +0 -1
  467. package/dist/operating-mode/deployment-name.d.ts +0 -1
  468. package/dist/operating-mode/deployment-name.js +0 -1
  469. package/dist/operating-mode/index.d.ts +5 -6
  470. package/dist/operating-mode/index.js +0 -1
  471. package/dist/operating-mode/mode-env.d.ts +1 -2
  472. package/dist/operating-mode/mode-env.js +0 -1
  473. package/dist/operating-mode/types.d.ts +2 -3
  474. package/dist/operating-mode/types.js +0 -1
  475. package/dist/paths/branch-name.d.ts +1 -2
  476. package/dist/paths/branch-name.js +0 -1
  477. package/dist/paths/branch.d.ts +3 -4
  478. package/dist/paths/branch.js +0 -1
  479. package/dist/paths/index.d.ts +8 -9
  480. package/dist/paths/index.js +0 -1
  481. package/dist/paths/normalize-server.d.ts +1 -2
  482. package/dist/paths/normalize-server.js +0 -1
  483. package/dist/paths/normalize.d.ts +1 -2
  484. package/dist/paths/normalize.js +0 -1
  485. package/dist/paths/resolve.d.ts +2 -3
  486. package/dist/paths/resolve.js +0 -1
  487. package/dist/paths/types.d.ts +0 -1
  488. package/dist/paths/types.js +0 -1
  489. package/dist/paths/validation.d.ts +1 -2
  490. package/dist/paths/validation.js +0 -1
  491. package/dist/reference-resolver.d.ts +3 -4
  492. package/dist/reference-resolver.js +0 -1
  493. package/dist/resolve-canopy-user.d.ts +3 -4
  494. package/dist/resolve-canopy-user.js +0 -1
  495. package/dist/resource-generation.d.ts +0 -1
  496. package/dist/resource-generation.js +0 -1
  497. package/dist/schema/index.d.ts +3 -4
  498. package/dist/schema/index.js +0 -1
  499. package/dist/schema/meta-loader.d.ts +3 -4
  500. package/dist/schema/meta-loader.js +3 -3
  501. package/dist/schema/resolver.d.ts +2 -3
  502. package/dist/schema/resolver.js +0 -1
  503. package/dist/schema/schema-store-types.d.ts +2 -3
  504. package/dist/schema/schema-store-types.js +0 -1
  505. package/dist/schema/schema-store.d.ts +6 -7
  506. package/dist/schema/schema-store.js +33 -1
  507. package/dist/schema/types.d.ts +1 -2
  508. package/dist/schema/types.js +0 -1
  509. package/dist/server.d.ts +54 -45
  510. package/dist/server.js +11 -3
  511. package/dist/services.d.ts +10 -11
  512. package/dist/services.js +0 -1
  513. package/dist/settings-workspace.d.ts +12 -15
  514. package/dist/settings-workspace.js +10 -13
  515. package/dist/static/index.d.ts +164 -6
  516. package/dist/static/index.js +242 -7
  517. package/dist/static/seo.d.ts +8 -30
  518. package/dist/static/seo.js +19 -46
  519. package/dist/sync-core.d.ts +0 -1
  520. package/dist/sync-core.js +0 -1
  521. package/dist/task-queue/index.d.ts +2 -3
  522. package/dist/task-queue/index.js +0 -1
  523. package/dist/task-queue/task-queue.d.ts +1 -2
  524. package/dist/task-queue/task-queue.js +0 -1
  525. package/dist/task-queue/types.d.ts +0 -1
  526. package/dist/task-queue/types.js +0 -1
  527. package/dist/types.d.ts +3 -4
  528. package/dist/types.js +0 -1
  529. package/dist/url-collision.d.ts +86 -0
  530. package/dist/url-collision.js +186 -0
  531. package/dist/url-path-resolver.d.ts +11 -2
  532. package/dist/url-path-resolver.js +37 -5
  533. package/dist/user.d.ts +3 -4
  534. package/dist/user.js +0 -1
  535. package/dist/utils/async-mutex.d.ts +0 -1
  536. package/dist/utils/async-mutex.js +0 -1
  537. package/dist/utils/atomic-write.d.ts +0 -1
  538. package/dist/utils/atomic-write.js +0 -1
  539. package/dist/utils/body-field.d.ts +12 -2
  540. package/dist/utils/body-field.js +17 -1
  541. package/dist/utils/content-serialize.d.ts +37 -0
  542. package/dist/utils/content-serialize.js +429 -0
  543. package/dist/utils/content-write-lock.d.ts +12 -16
  544. package/dist/utils/content-write-lock.js +38 -22
  545. package/dist/utils/debug.d.ts +0 -1
  546. package/dist/utils/debug.js +0 -1
  547. package/dist/utils/entry-url.d.ts +17 -1
  548. package/dist/utils/entry-url.js +21 -3
  549. package/dist/utils/error.d.ts +0 -1
  550. package/dist/utils/error.js +0 -1
  551. package/dist/utils/flatten-group-fields.d.ts +1 -2
  552. package/dist/utils/flatten-group-fields.js +0 -1
  553. package/dist/utils/format.d.ts +1 -2
  554. package/dist/utils/format.js +0 -1
  555. package/dist/utils/fs.d.ts +7 -1
  556. package/dist/utils/fs.js +16 -1
  557. package/dist/utils/git.d.ts +1 -2
  558. package/dist/utils/git.js +0 -1
  559. package/dist/utils/logger.d.ts +0 -1
  560. package/dist/utils/logger.js +0 -1
  561. package/dist/utils/occ-json-write.d.ts +0 -1
  562. package/dist/utils/occ-json-write.js +0 -1
  563. package/dist/utils/provisioning-lock.d.ts +17 -4
  564. package/dist/utils/provisioning-lock.js +94 -14
  565. package/dist/utils/sanitize-href.d.ts +2 -3
  566. package/dist/utils/sanitize-href.js +2 -3
  567. package/dist/utils/title-field.d.ts +1 -2
  568. package/dist/utils/title-field.js +0 -1
  569. package/dist/utils/typed-filename.d.ts +61 -0
  570. package/dist/utils/typed-filename.js +92 -0
  571. package/dist/utils/url-prefix.d.ts +121 -0
  572. package/dist/utils/url-prefix.js +158 -0
  573. package/dist/validation/block-structural-keys.d.ts +42 -0
  574. package/dist/validation/block-structural-keys.js +44 -0
  575. package/dist/validation/deletion-checker.d.ts +4 -5
  576. package/dist/validation/deletion-checker.js +0 -1
  577. package/dist/validation/entry-link-validator.d.ts +2 -3
  578. package/dist/validation/entry-link-validator.js +0 -1
  579. package/dist/validation/entry-type-reference-validator.d.ts +1 -2
  580. package/dist/validation/entry-type-reference-validator.js +0 -1
  581. package/dist/validation/entry-validator.d.ts +23 -2
  582. package/dist/validation/entry-validator.js +34 -2
  583. package/dist/validation/field-traversal.d.ts +28 -4
  584. package/dist/validation/field-traversal.js +24 -7
  585. package/dist/validation/reference-validator.d.ts +2 -3
  586. package/dist/validation/reference-validator.js +0 -1
  587. package/dist/worker/cms-worker.d.ts +9 -2
  588. package/dist/worker/cms-worker.js +59 -2
  589. package/dist/worker/log.d.ts +0 -1
  590. package/dist/worker/log.js +0 -1
  591. package/dist/worker/task-queue-config.d.ts +1 -2
  592. package/dist/worker/task-queue-config.js +0 -1
  593. package/dist/worker/task-queue.d.ts +3 -4
  594. package/dist/worker/task-queue.js +0 -1
  595. package/dist/worker/worker-status.d.ts +1 -2
  596. package/dist/worker/worker-status.js +0 -1
  597. package/package.json +2 -6
  598. package/dist/ai/generate.d.ts.map +0 -1
  599. package/dist/ai/generate.js.map +0 -1
  600. package/dist/ai/handler.d.ts.map +0 -1
  601. package/dist/ai/handler.js.map +0 -1
  602. package/dist/ai/index.d.ts.map +0 -1
  603. package/dist/ai/index.js.map +0 -1
  604. package/dist/ai/json-to-markdown.d.ts.map +0 -1
  605. package/dist/ai/json-to-markdown.js.map +0 -1
  606. package/dist/ai/resolve-branch.d.ts.map +0 -1
  607. package/dist/ai/resolve-branch.js.map +0 -1
  608. package/dist/ai/strip-mdx.d.ts.map +0 -1
  609. package/dist/ai/strip-mdx.js.map +0 -1
  610. package/dist/ai/to-plain-text.d.ts.map +0 -1
  611. package/dist/ai/to-plain-text.js.map +0 -1
  612. package/dist/ai/transform-components.d.ts.map +0 -1
  613. package/dist/ai/transform-components.js.map +0 -1
  614. package/dist/ai/types.d.ts.map +0 -1
  615. package/dist/ai/types.js.map +0 -1
  616. package/dist/api/__test__/mock-client.d.ts +0 -231
  617. package/dist/api/__test__/mock-client.d.ts.map +0 -1
  618. package/dist/api/__test__/mock-client.js +0 -388
  619. package/dist/api/__test__/mock-client.js.map +0 -1
  620. package/dist/api/admin-branch-health.d.ts.map +0 -1
  621. package/dist/api/admin-branch-health.js.map +0 -1
  622. package/dist/api/admin.d.ts.map +0 -1
  623. package/dist/api/admin.js.map +0 -1
  624. package/dist/api/assets.d.ts.map +0 -1
  625. package/dist/api/assets.js.map +0 -1
  626. package/dist/api/branch-merge.d.ts.map +0 -1
  627. package/dist/api/branch-merge.js.map +0 -1
  628. package/dist/api/branch-review.d.ts.map +0 -1
  629. package/dist/api/branch-review.js.map +0 -1
  630. package/dist/api/branch-status.d.ts.map +0 -1
  631. package/dist/api/branch-status.js.map +0 -1
  632. package/dist/api/branch-withdraw.d.ts.map +0 -1
  633. package/dist/api/branch-withdraw.js.map +0 -1
  634. package/dist/api/branch.d.ts.map +0 -1
  635. package/dist/api/branch.js.map +0 -1
  636. package/dist/api/client.d.ts.map +0 -1
  637. package/dist/api/client.js.map +0 -1
  638. package/dist/api/comments.d.ts.map +0 -1
  639. package/dist/api/comments.js.map +0 -1
  640. package/dist/api/content.d.ts.map +0 -1
  641. package/dist/api/content.js.map +0 -1
  642. package/dist/api/entries-constants.d.ts.map +0 -1
  643. package/dist/api/entries-constants.js.map +0 -1
  644. package/dist/api/entries.d.ts.map +0 -1
  645. package/dist/api/entries.js.map +0 -1
  646. package/dist/api/github-sync.d.ts.map +0 -1
  647. package/dist/api/github-sync.js.map +0 -1
  648. package/dist/api/groups.d.ts.map +0 -1
  649. package/dist/api/groups.js.map +0 -1
  650. package/dist/api/guards.d.ts.map +0 -1
  651. package/dist/api/guards.js.map +0 -1
  652. package/dist/api/index.d.ts.map +0 -1
  653. package/dist/api/index.js.map +0 -1
  654. package/dist/api/permissions.d.ts.map +0 -1
  655. package/dist/api/permissions.js.map +0 -1
  656. package/dist/api/reference-options.d.ts.map +0 -1
  657. package/dist/api/reference-options.js.map +0 -1
  658. package/dist/api/request-body-hash.d.ts.map +0 -1
  659. package/dist/api/request-body-hash.js.map +0 -1
  660. package/dist/api/resolve-references.d.ts.map +0 -1
  661. package/dist/api/resolve-references.js.map +0 -1
  662. package/dist/api/route-builder.d.ts.map +0 -1
  663. package/dist/api/route-builder.js.map +0 -1
  664. package/dist/api/schema.d.ts.map +0 -1
  665. package/dist/api/schema.js.map +0 -1
  666. package/dist/api/settings-helpers.d.ts.map +0 -1
  667. package/dist/api/settings-helpers.js.map +0 -1
  668. package/dist/api/types.d.ts.map +0 -1
  669. package/dist/api/types.js.map +0 -1
  670. package/dist/api/user.d.ts.map +0 -1
  671. package/dist/api/user.js.map +0 -1
  672. package/dist/api/validators.d.ts.map +0 -1
  673. package/dist/api/validators.js.map +0 -1
  674. package/dist/assets/asset-prefixes.d.ts.map +0 -1
  675. package/dist/assets/asset-prefixes.js.map +0 -1
  676. package/dist/assets/asset-src.d.ts.map +0 -1
  677. package/dist/assets/asset-src.js.map +0 -1
  678. package/dist/assets/asset-url.d.ts.map +0 -1
  679. package/dist/assets/asset-url.js.map +0 -1
  680. package/dist/assets/factory.d.ts.map +0 -1
  681. package/dist/assets/factory.js.map +0 -1
  682. package/dist/assets/finalize.d.ts.map +0 -1
  683. package/dist/assets/finalize.js.map +0 -1
  684. package/dist/assets/index.d.ts.map +0 -1
  685. package/dist/assets/index.js.map +0 -1
  686. package/dist/assets/keys.d.ts.map +0 -1
  687. package/dist/assets/keys.js.map +0 -1
  688. package/dist/assets/pipeline.d.ts.map +0 -1
  689. package/dist/assets/pipeline.js.map +0 -1
  690. package/dist/assets/store-local.d.ts.map +0 -1
  691. package/dist/assets/store-local.js.map +0 -1
  692. package/dist/assets/store-s3.d.ts.map +0 -1
  693. package/dist/assets/store-s3.js.map +0 -1
  694. package/dist/assets/svg-sanitizer.d.ts.map +0 -1
  695. package/dist/assets/svg-sanitizer.js.map +0 -1
  696. package/dist/assets/transform-directives.d.ts.map +0 -1
  697. package/dist/assets/transform-directives.js.map +0 -1
  698. package/dist/assets/transform.d.ts.map +0 -1
  699. package/dist/assets/transform.js.map +0 -1
  700. package/dist/assets/types.d.ts.map +0 -1
  701. package/dist/assets/types.js.map +0 -1
  702. package/dist/auth/cache.d.ts.map +0 -1
  703. package/dist/auth/cache.js.map +0 -1
  704. package/dist/auth/caching-auth-plugin.d.ts.map +0 -1
  705. package/dist/auth/caching-auth-plugin.js.map +0 -1
  706. package/dist/auth/context-helpers.d.ts.map +0 -1
  707. package/dist/auth/context-helpers.js.map +0 -1
  708. package/dist/auth/file-based-auth-cache.d.ts.map +0 -1
  709. package/dist/auth/file-based-auth-cache.js.map +0 -1
  710. package/dist/auth/index.d.ts.map +0 -1
  711. package/dist/auth/index.js.map +0 -1
  712. package/dist/auth/plugin.d.ts.map +0 -1
  713. package/dist/auth/plugin.js.map +0 -1
  714. package/dist/auth/types.d.ts.map +0 -1
  715. package/dist/auth/types.js.map +0 -1
  716. package/dist/authorization/branch.d.ts.map +0 -1
  717. package/dist/authorization/branch.js.map +0 -1
  718. package/dist/authorization/content.d.ts.map +0 -1
  719. package/dist/authorization/content.js.map +0 -1
  720. package/dist/authorization/groups/index.d.ts.map +0 -1
  721. package/dist/authorization/groups/index.js.map +0 -1
  722. package/dist/authorization/groups/loader.d.ts.map +0 -1
  723. package/dist/authorization/groups/loader.js.map +0 -1
  724. package/dist/authorization/groups/schema.d.ts.map +0 -1
  725. package/dist/authorization/groups/schema.js.map +0 -1
  726. package/dist/authorization/helpers.d.ts.map +0 -1
  727. package/dist/authorization/helpers.js.map +0 -1
  728. package/dist/authorization/index.d.ts.map +0 -1
  729. package/dist/authorization/index.js.map +0 -1
  730. package/dist/authorization/path.d.ts.map +0 -1
  731. package/dist/authorization/path.js.map +0 -1
  732. package/dist/authorization/permissions/index.d.ts.map +0 -1
  733. package/dist/authorization/permissions/index.js.map +0 -1
  734. package/dist/authorization/permissions/loader.d.ts.map +0 -1
  735. package/dist/authorization/permissions/loader.js.map +0 -1
  736. package/dist/authorization/permissions/schema.d.ts.map +0 -1
  737. package/dist/authorization/permissions/schema.js.map +0 -1
  738. package/dist/authorization/protected-branch.d.ts.map +0 -1
  739. package/dist/authorization/protected-branch.js.map +0 -1
  740. package/dist/authorization/settings-file-store.d.ts.map +0 -1
  741. package/dist/authorization/settings-file-store.js.map +0 -1
  742. package/dist/authorization/test-utils.d.ts +0 -26
  743. package/dist/authorization/test-utils.d.ts.map +0 -1
  744. package/dist/authorization/test-utils.js +0 -23
  745. package/dist/authorization/test-utils.js.map +0 -1
  746. package/dist/authorization/types.d.ts.map +0 -1
  747. package/dist/authorization/types.js.map +0 -1
  748. package/dist/authorization/validation.d.ts.map +0 -1
  749. package/dist/authorization/validation.js.map +0 -1
  750. package/dist/branch-health.d.ts.map +0 -1
  751. package/dist/branch-health.js.map +0 -1
  752. package/dist/branch-metadata.d.ts.map +0 -1
  753. package/dist/branch-metadata.js.map +0 -1
  754. package/dist/branch-registry.d.ts.map +0 -1
  755. package/dist/branch-registry.js.map +0 -1
  756. package/dist/branch-schema-cache.d.ts.map +0 -1
  757. package/dist/branch-schema-cache.js.map +0 -1
  758. package/dist/branch-workspace.d.ts.map +0 -1
  759. package/dist/branch-workspace.js.map +0 -1
  760. package/dist/build/generate-ai-content.d.ts.map +0 -1
  761. package/dist/build/generate-ai-content.js.map +0 -1
  762. package/dist/build/index.d.ts.map +0 -1
  763. package/dist/build/index.js.map +0 -1
  764. package/dist/build-canopy.d.ts.map +0 -1
  765. package/dist/build-canopy.js.map +0 -1
  766. package/dist/build-mode.d.ts.map +0 -1
  767. package/dist/build-mode.js.map +0 -1
  768. package/dist/cli/cli.d.ts.map +0 -1
  769. package/dist/cli/cli.js.map +0 -1
  770. package/dist/cli/generate-ai-content.d.ts.map +0 -1
  771. package/dist/cli/generate-ai-content.js.map +0 -1
  772. package/dist/cli/init.d.ts.map +0 -1
  773. package/dist/cli/init.js.map +0 -1
  774. package/dist/cli/migrate.d.ts.map +0 -1
  775. package/dist/cli/migrate.js.map +0 -1
  776. package/dist/cli/project-detect.d.ts.map +0 -1
  777. package/dist/cli/project-detect.js.map +0 -1
  778. package/dist/cli/project-root.d.ts.map +0 -1
  779. package/dist/cli/project-root.js.map +0 -1
  780. package/dist/cli/sync.d.ts.map +0 -1
  781. package/dist/cli/sync.js.map +0 -1
  782. package/dist/cli/templates.d.ts.map +0 -1
  783. package/dist/cli/templates.js.map +0 -1
  784. package/dist/client.d.ts.map +0 -1
  785. package/dist/client.js.map +0 -1
  786. package/dist/comment-store.d.ts.map +0 -1
  787. package/dist/comment-store.js.map +0 -1
  788. package/dist/config/flatten.d.ts.map +0 -1
  789. package/dist/config/flatten.js.map +0 -1
  790. package/dist/config/helpers.d.ts.map +0 -1
  791. package/dist/config/helpers.js.map +0 -1
  792. package/dist/config/index.d.ts.map +0 -1
  793. package/dist/config/index.js.map +0 -1
  794. package/dist/config/schemas/collection.d.ts.map +0 -1
  795. package/dist/config/schemas/collection.js.map +0 -1
  796. package/dist/config/schemas/config.d.ts.map +0 -1
  797. package/dist/config/schemas/config.js.map +0 -1
  798. package/dist/config/schemas/field.d.ts.map +0 -1
  799. package/dist/config/schemas/field.js.map +0 -1
  800. package/dist/config/schemas/media.d.ts.map +0 -1
  801. package/dist/config/schemas/media.js.map +0 -1
  802. package/dist/config/schemas/permissions.d.ts.map +0 -1
  803. package/dist/config/schemas/permissions.js.map +0 -1
  804. package/dist/config/types.d.ts.map +0 -1
  805. package/dist/config/types.js.map +0 -1
  806. package/dist/config/validation.d.ts.map +0 -1
  807. package/dist/config/validation.js.map +0 -1
  808. package/dist/config-test.d.ts +0 -20
  809. package/dist/config-test.d.ts.map +0 -1
  810. package/dist/config-test.js +0 -49
  811. package/dist/config-test.js.map +0 -1
  812. package/dist/config.d.ts.map +0 -1
  813. package/dist/config.js.map +0 -1
  814. package/dist/content-id-index.d.ts.map +0 -1
  815. package/dist/content-id-index.js.map +0 -1
  816. package/dist/content-index-generation.d.ts.map +0 -1
  817. package/dist/content-index-generation.js.map +0 -1
  818. package/dist/content-index-registry.d.ts.map +0 -1
  819. package/dist/content-index-registry.js.map +0 -1
  820. package/dist/content-listing.d.ts.map +0 -1
  821. package/dist/content-listing.js.map +0 -1
  822. package/dist/content-reader.d.ts.map +0 -1
  823. package/dist/content-reader.js.map +0 -1
  824. package/dist/content-store.d.ts.map +0 -1
  825. package/dist/content-store.js.map +0 -1
  826. package/dist/content-tree.d.ts.map +0 -1
  827. package/dist/content-tree.js.map +0 -1
  828. package/dist/context.d.ts.map +0 -1
  829. package/dist/context.js.map +0 -1
  830. package/dist/dev-content-watcher.d.ts.map +0 -1
  831. package/dist/dev-content-watcher.js.map +0 -1
  832. package/dist/editor/BranchManager.d.ts.map +0 -1
  833. package/dist/editor/BranchManager.js.map +0 -1
  834. package/dist/editor/CanopyEditor.d.ts.map +0 -1
  835. package/dist/editor/CanopyEditor.js.map +0 -1
  836. package/dist/editor/CanopyEditorPage.d.ts.map +0 -1
  837. package/dist/editor/CanopyEditorPage.js.map +0 -1
  838. package/dist/editor/CommentsPanel.d.ts.map +0 -1
  839. package/dist/editor/CommentsPanel.js.map +0 -1
  840. package/dist/editor/Editor.d.ts.map +0 -1
  841. package/dist/editor/Editor.js.map +0 -1
  842. package/dist/editor/EditorPanes.d.ts.map +0 -1
  843. package/dist/editor/EditorPanes.js.map +0 -1
  844. package/dist/editor/EntryNavigator.d.ts.map +0 -1
  845. package/dist/editor/EntryNavigator.js.map +0 -1
  846. package/dist/editor/FormRenderer.d.ts.map +0 -1
  847. package/dist/editor/FormRenderer.js.map +0 -1
  848. package/dist/editor/GroupManager.d.ts.map +0 -1
  849. package/dist/editor/GroupManager.js.map +0 -1
  850. package/dist/editor/PermissionManager.d.ts.map +0 -1
  851. package/dist/editor/PermissionManager.js.map +0 -1
  852. package/dist/editor/admin/SystemHealthPanel.d.ts.map +0 -1
  853. package/dist/editor/admin/SystemHealthPanel.js.map +0 -1
  854. package/dist/editor/admin/useSystemHealth.d.ts.map +0 -1
  855. package/dist/editor/admin/useSystemHealth.js.map +0 -1
  856. package/dist/editor/canopy-path.d.ts.map +0 -1
  857. package/dist/editor/canopy-path.js.map +0 -1
  858. package/dist/editor/client-reference-resolver.d.ts.map +0 -1
  859. package/dist/editor/client-reference-resolver.js.map +0 -1
  860. package/dist/editor/comments/BranchComments.d.ts.map +0 -1
  861. package/dist/editor/comments/BranchComments.js.map +0 -1
  862. package/dist/editor/comments/EntryComments.d.ts.map +0 -1
  863. package/dist/editor/comments/EntryComments.js.map +0 -1
  864. package/dist/editor/comments/FieldWrapper.d.ts.map +0 -1
  865. package/dist/editor/comments/FieldWrapper.js.map +0 -1
  866. package/dist/editor/comments/InlineCommentThread.d.ts.map +0 -1
  867. package/dist/editor/comments/InlineCommentThread.js.map +0 -1
  868. package/dist/editor/comments/ThreadCarousel.d.ts.map +0 -1
  869. package/dist/editor/comments/ThreadCarousel.js.map +0 -1
  870. package/dist/editor/components/ConfirmDeleteModal.d.ts.map +0 -1
  871. package/dist/editor/components/ConfirmDeleteModal.js.map +0 -1
  872. package/dist/editor/components/EditorFooter.d.ts.map +0 -1
  873. package/dist/editor/components/EditorFooter.js.map +0 -1
  874. package/dist/editor/components/EditorHeader.d.ts.map +0 -1
  875. package/dist/editor/components/EditorHeader.js.map +0 -1
  876. package/dist/editor/components/EditorSidebar.d.ts.map +0 -1
  877. package/dist/editor/components/EditorSidebar.js.map +0 -1
  878. package/dist/editor/components/EntryCreateModal.d.ts.map +0 -1
  879. package/dist/editor/components/EntryCreateModal.js.map +0 -1
  880. package/dist/editor/components/RenameEntryModal.d.ts.map +0 -1
  881. package/dist/editor/components/RenameEntryModal.js.map +0 -1
  882. package/dist/editor/components/UserBadge.d.ts.map +0 -1
  883. package/dist/editor/components/UserBadge.js.map +0 -1
  884. package/dist/editor/components/index.d.ts.map +0 -1
  885. package/dist/editor/components/index.js.map +0 -1
  886. package/dist/editor/context/ApiClientContext.d.ts.map +0 -1
  887. package/dist/editor/context/ApiClientContext.js.map +0 -1
  888. package/dist/editor/context/AssetContext.d.ts.map +0 -1
  889. package/dist/editor/context/AssetContext.js.map +0 -1
  890. package/dist/editor/context/EditorStateContext.d.ts.map +0 -1
  891. package/dist/editor/context/EditorStateContext.js.map +0 -1
  892. package/dist/editor/context/SWRProvider.d.ts.map +0 -1
  893. package/dist/editor/context/SWRProvider.js.map +0 -1
  894. package/dist/editor/context/index.d.ts.map +0 -1
  895. package/dist/editor/context/index.js.map +0 -1
  896. package/dist/editor/editor-config.d.ts.map +0 -1
  897. package/dist/editor/editor-config.js.map +0 -1
  898. package/dist/editor/editor-utils.d.ts.map +0 -1
  899. package/dist/editor/editor-utils.js.map +0 -1
  900. package/dist/editor/fields/BlockField.d.ts.map +0 -1
  901. package/dist/editor/fields/BlockField.js.map +0 -1
  902. package/dist/editor/fields/CodeField.d.ts.map +0 -1
  903. package/dist/editor/fields/CodeField.js.map +0 -1
  904. package/dist/editor/fields/DateTimeField.d.ts.map +0 -1
  905. package/dist/editor/fields/DateTimeField.js.map +0 -1
  906. package/dist/editor/fields/ImageField.d.ts.map +0 -1
  907. package/dist/editor/fields/ImageField.js.map +0 -1
  908. package/dist/editor/fields/InlineGroupField.d.ts.map +0 -1
  909. package/dist/editor/fields/InlineGroupField.js.map +0 -1
  910. package/dist/editor/fields/MarkdownField.d.ts.map +0 -1
  911. package/dist/editor/fields/MarkdownField.js.map +0 -1
  912. package/dist/editor/fields/MdxImageDialog.d.ts.map +0 -1
  913. package/dist/editor/fields/MdxImageDialog.js.map +0 -1
  914. package/dist/editor/fields/NumberField.d.ts.map +0 -1
  915. package/dist/editor/fields/NumberField.js.map +0 -1
  916. package/dist/editor/fields/NumberListField.d.ts.map +0 -1
  917. package/dist/editor/fields/NumberListField.js.map +0 -1
  918. package/dist/editor/fields/ObjectField.d.ts.map +0 -1
  919. package/dist/editor/fields/ObjectField.js.map +0 -1
  920. package/dist/editor/fields/ReferenceField.d.ts.map +0 -1
  921. package/dist/editor/fields/ReferenceField.js.map +0 -1
  922. package/dist/editor/fields/SelectField.d.ts.map +0 -1
  923. package/dist/editor/fields/SelectField.js.map +0 -1
  924. package/dist/editor/fields/StringListField.d.ts.map +0 -1
  925. package/dist/editor/fields/StringListField.js.map +0 -1
  926. package/dist/editor/fields/TextField.d.ts.map +0 -1
  927. package/dist/editor/fields/TextField.js.map +0 -1
  928. package/dist/editor/fields/ToggleField.d.ts.map +0 -1
  929. package/dist/editor/fields/ToggleField.js.map +0 -1
  930. package/dist/editor/fields/entry-link/EntryLinkContext.d.ts.map +0 -1
  931. package/dist/editor/fields/entry-link/EntryLinkContext.js.map +0 -1
  932. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts.map +0 -1
  933. package/dist/editor/fields/entry-link/InsertEntryLink.js.map +0 -1
  934. package/dist/editor/fields/entry-link/index.d.ts.map +0 -1
  935. package/dist/editor/fields/entry-link/index.js.map +0 -1
  936. package/dist/editor/group-manager/ExternalGroupsTab.d.ts.map +0 -1
  937. package/dist/editor/group-manager/ExternalGroupsTab.js.map +0 -1
  938. package/dist/editor/group-manager/GroupCard.d.ts.map +0 -1
  939. package/dist/editor/group-manager/GroupCard.js.map +0 -1
  940. package/dist/editor/group-manager/GroupForm.d.ts.map +0 -1
  941. package/dist/editor/group-manager/GroupForm.js.map +0 -1
  942. package/dist/editor/group-manager/InternalGroupsTab.d.ts.map +0 -1
  943. package/dist/editor/group-manager/InternalGroupsTab.js.map +0 -1
  944. package/dist/editor/group-manager/MemberList.d.ts.map +0 -1
  945. package/dist/editor/group-manager/MemberList.js.map +0 -1
  946. package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts.map +0 -1
  947. package/dist/editor/group-manager/hooks/useExternalGroupSearch.js.map +0 -1
  948. package/dist/editor/group-manager/hooks/useGroupState.d.ts.map +0 -1
  949. package/dist/editor/group-manager/hooks/useGroupState.js.map +0 -1
  950. package/dist/editor/group-manager/hooks/useUserSearch.d.ts.map +0 -1
  951. package/dist/editor/group-manager/hooks/useUserSearch.js.map +0 -1
  952. package/dist/editor/group-manager/index.d.ts.map +0 -1
  953. package/dist/editor/group-manager/index.js.map +0 -1
  954. package/dist/editor/group-manager/types.d.ts.map +0 -1
  955. package/dist/editor/group-manager/types.js.map +0 -1
  956. package/dist/editor/hooks/__test__/test-utils.d.ts +0 -164
  957. package/dist/editor/hooks/__test__/test-utils.d.ts.map +0 -1
  958. package/dist/editor/hooks/__test__/test-utils.js +0 -203
  959. package/dist/editor/hooks/__test__/test-utils.js.map +0 -1
  960. package/dist/editor/hooks/index.d.ts.map +0 -1
  961. package/dist/editor/hooks/index.js.map +0 -1
  962. package/dist/editor/hooks/useBranchActions.d.ts.map +0 -1
  963. package/dist/editor/hooks/useBranchActions.js.map +0 -1
  964. package/dist/editor/hooks/useBranchManager.d.ts.map +0 -1
  965. package/dist/editor/hooks/useBranchManager.js.map +0 -1
  966. package/dist/editor/hooks/useBranchesData.d.ts.map +0 -1
  967. package/dist/editor/hooks/useBranchesData.js.map +0 -1
  968. package/dist/editor/hooks/useCommentSystem.d.ts.map +0 -1
  969. package/dist/editor/hooks/useCommentSystem.js.map +0 -1
  970. package/dist/editor/hooks/useCommentsData.d.ts.map +0 -1
  971. package/dist/editor/hooks/useCommentsData.js.map +0 -1
  972. package/dist/editor/hooks/useDraftManager.d.ts.map +0 -1
  973. package/dist/editor/hooks/useDraftManager.js.map +0 -1
  974. package/dist/editor/hooks/useEditorLayout.d.ts.map +0 -1
  975. package/dist/editor/hooks/useEditorLayout.js.map +0 -1
  976. package/dist/editor/hooks/useEntriesData.d.ts.map +0 -1
  977. package/dist/editor/hooks/useEntriesData.js.map +0 -1
  978. package/dist/editor/hooks/useEntryLinkResolution.d.ts.map +0 -1
  979. package/dist/editor/hooks/useEntryLinkResolution.js.map +0 -1
  980. package/dist/editor/hooks/useEntryManager.d.ts.map +0 -1
  981. package/dist/editor/hooks/useEntryManager.js.map +0 -1
  982. package/dist/editor/hooks/useGroupManager.d.ts.map +0 -1
  983. package/dist/editor/hooks/useGroupManager.js.map +0 -1
  984. package/dist/editor/hooks/usePermissionManager.d.ts.map +0 -1
  985. package/dist/editor/hooks/usePermissionManager.js.map +0 -1
  986. package/dist/editor/hooks/useReferenceResolution.d.ts.map +0 -1
  987. package/dist/editor/hooks/useReferenceResolution.js.map +0 -1
  988. package/dist/editor/hooks/useSchemaManager.d.ts.map +0 -1
  989. package/dist/editor/hooks/useSchemaManager.js.map +0 -1
  990. package/dist/editor/hooks/useUserContext.d.ts.map +0 -1
  991. package/dist/editor/hooks/useUserContext.js.map +0 -1
  992. package/dist/editor/hooks/useUserMetadata.d.ts.map +0 -1
  993. package/dist/editor/hooks/useUserMetadata.js.map +0 -1
  994. package/dist/editor/media/AssetCard.d.ts.map +0 -1
  995. package/dist/editor/media/AssetCard.js.map +0 -1
  996. package/dist/editor/media/CropStep.d.ts.map +0 -1
  997. package/dist/editor/media/CropStep.js.map +0 -1
  998. package/dist/editor/media/MediaLibrary.d.ts.map +0 -1
  999. package/dist/editor/media/MediaLibrary.js.map +0 -1
  1000. package/dist/editor/media/MediaLibraryBody.d.ts.map +0 -1
  1001. package/dist/editor/media/MediaLibraryBody.js.map +0 -1
  1002. package/dist/editor/media/crop-math.d.ts.map +0 -1
  1003. package/dist/editor/media/crop-math.js.map +0 -1
  1004. package/dist/editor/media/upload-asset.d.ts.map +0 -1
  1005. package/dist/editor/media/upload-asset.js.map +0 -1
  1006. package/dist/editor/media/upload-constants.d.ts.map +0 -1
  1007. package/dist/editor/media/upload-constants.js.map +0 -1
  1008. package/dist/editor/media/useAssetUpload.d.ts.map +0 -1
  1009. package/dist/editor/media/useAssetUpload.js.map +0 -1
  1010. package/dist/editor/media/xhr-upload.d.ts.map +0 -1
  1011. package/dist/editor/media/xhr-upload.js.map +0 -1
  1012. package/dist/editor/permission-manager/GroupSelector.d.ts.map +0 -1
  1013. package/dist/editor/permission-manager/GroupSelector.js.map +0 -1
  1014. package/dist/editor/permission-manager/PermissionEditor.d.ts.map +0 -1
  1015. package/dist/editor/permission-manager/PermissionEditor.js.map +0 -1
  1016. package/dist/editor/permission-manager/PermissionLevelBadge.d.ts.map +0 -1
  1017. package/dist/editor/permission-manager/PermissionLevelBadge.js.map +0 -1
  1018. package/dist/editor/permission-manager/PermissionTree.d.ts.map +0 -1
  1019. package/dist/editor/permission-manager/PermissionTree.js.map +0 -1
  1020. package/dist/editor/permission-manager/UserSelector.d.ts.map +0 -1
  1021. package/dist/editor/permission-manager/UserSelector.js.map +0 -1
  1022. package/dist/editor/permission-manager/constants.d.ts.map +0 -1
  1023. package/dist/editor/permission-manager/constants.js.map +0 -1
  1024. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts.map +0 -1
  1025. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js.map +0 -1
  1026. package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts.map +0 -1
  1027. package/dist/editor/permission-manager/hooks/usePermissionTree.js.map +0 -1
  1028. package/dist/editor/permission-manager/index.d.ts.map +0 -1
  1029. package/dist/editor/permission-manager/index.js.map +0 -1
  1030. package/dist/editor/permission-manager/types.d.ts.map +0 -1
  1031. package/dist/editor/permission-manager/types.js.map +0 -1
  1032. package/dist/editor/permission-manager/utils.d.ts.map +0 -1
  1033. package/dist/editor/permission-manager/utils.js.map +0 -1
  1034. package/dist/editor/preview-bridge.d.ts.map +0 -1
  1035. package/dist/editor/preview-bridge.js.map +0 -1
  1036. package/dist/editor/relative-time.d.ts.map +0 -1
  1037. package/dist/editor/relative-time.js.map +0 -1
  1038. package/dist/editor/schema-editor/CollectionEditor.d.ts.map +0 -1
  1039. package/dist/editor/schema-editor/CollectionEditor.js.map +0 -1
  1040. package/dist/editor/schema-editor/EntryTypeEditor.d.ts.map +0 -1
  1041. package/dist/editor/schema-editor/EntryTypeEditor.js.map +0 -1
  1042. package/dist/editor/schema-editor/index.d.ts.map +0 -1
  1043. package/dist/editor/schema-editor/index.js.map +0 -1
  1044. package/dist/editor/setup-test-dom.d.ts +0 -1
  1045. package/dist/editor/setup-test-dom.d.ts.map +0 -1
  1046. package/dist/editor/setup-test-dom.js +0 -12
  1047. package/dist/editor/setup-test-dom.js.map +0 -1
  1048. package/dist/editor/test-setup.d.ts +0 -6
  1049. package/dist/editor/test-setup.d.ts.map +0 -1
  1050. package/dist/editor/test-setup.js +0 -42
  1051. package/dist/editor/test-setup.js.map +0 -1
  1052. package/dist/editor/theme.d.ts.map +0 -1
  1053. package/dist/editor/theme.js.map +0 -1
  1054. package/dist/editor/utils/env.d.ts.map +0 -1
  1055. package/dist/editor/utils/env.js.map +0 -1
  1056. package/dist/entry-link-resolver.d.ts.map +0 -1
  1057. package/dist/entry-link-resolver.js.map +0 -1
  1058. package/dist/entry-schema-registry.d.ts.map +0 -1
  1059. package/dist/entry-schema-registry.js.map +0 -1
  1060. package/dist/entry-schema.d.ts.map +0 -1
  1061. package/dist/entry-schema.js.map +0 -1
  1062. package/dist/git-manager.d.ts.map +0 -1
  1063. package/dist/git-manager.js.map +0 -1
  1064. package/dist/github-service.d.ts.map +0 -1
  1065. package/dist/github-service.js.map +0 -1
  1066. package/dist/http/handler.d.ts.map +0 -1
  1067. package/dist/http/handler.js.map +0 -1
  1068. package/dist/http/index.d.ts.map +0 -1
  1069. package/dist/http/index.js.map +0 -1
  1070. package/dist/http/router.d.ts.map +0 -1
  1071. package/dist/http/router.js.map +0 -1
  1072. package/dist/http/types.d.ts.map +0 -1
  1073. package/dist/http/types.js.map +0 -1
  1074. package/dist/id.d.ts.map +0 -1
  1075. package/dist/id.js.map +0 -1
  1076. package/dist/index.d.ts.map +0 -1
  1077. package/dist/index.js.map +0 -1
  1078. package/dist/operating-mode/client-safe-strategy.d.ts.map +0 -1
  1079. package/dist/operating-mode/client-safe-strategy.js.map +0 -1
  1080. package/dist/operating-mode/client-unsafe-strategy.d.ts.map +0 -1
  1081. package/dist/operating-mode/client-unsafe-strategy.js.map +0 -1
  1082. package/dist/operating-mode/client.d.ts.map +0 -1
  1083. package/dist/operating-mode/client.js.map +0 -1
  1084. package/dist/operating-mode/deployment-name-fixtures.d.ts.map +0 -1
  1085. package/dist/operating-mode/deployment-name-fixtures.js.map +0 -1
  1086. package/dist/operating-mode/deployment-name.d.ts.map +0 -1
  1087. package/dist/operating-mode/deployment-name.js.map +0 -1
  1088. package/dist/operating-mode/index.d.ts.map +0 -1
  1089. package/dist/operating-mode/index.js.map +0 -1
  1090. package/dist/operating-mode/mode-env.d.ts.map +0 -1
  1091. package/dist/operating-mode/mode-env.js.map +0 -1
  1092. package/dist/operating-mode/types.d.ts.map +0 -1
  1093. package/dist/operating-mode/types.js.map +0 -1
  1094. package/dist/paths/branch-name.d.ts.map +0 -1
  1095. package/dist/paths/branch-name.js.map +0 -1
  1096. package/dist/paths/branch.d.ts.map +0 -1
  1097. package/dist/paths/branch.js.map +0 -1
  1098. package/dist/paths/index.d.ts.map +0 -1
  1099. package/dist/paths/index.js.map +0 -1
  1100. package/dist/paths/normalize-server.d.ts.map +0 -1
  1101. package/dist/paths/normalize-server.js.map +0 -1
  1102. package/dist/paths/normalize.d.ts.map +0 -1
  1103. package/dist/paths/normalize.js.map +0 -1
  1104. package/dist/paths/resolve.d.ts.map +0 -1
  1105. package/dist/paths/resolve.js.map +0 -1
  1106. package/dist/paths/test-utils.d.ts +0 -22
  1107. package/dist/paths/test-utils.d.ts.map +0 -1
  1108. package/dist/paths/test-utils.js +0 -21
  1109. package/dist/paths/test-utils.js.map +0 -1
  1110. package/dist/paths/types.d.ts.map +0 -1
  1111. package/dist/paths/types.js.map +0 -1
  1112. package/dist/paths/validation.d.ts.map +0 -1
  1113. package/dist/paths/validation.js.map +0 -1
  1114. package/dist/reference-resolver.d.ts.map +0 -1
  1115. package/dist/reference-resolver.js.map +0 -1
  1116. package/dist/resolve-canopy-user.d.ts.map +0 -1
  1117. package/dist/resolve-canopy-user.js.map +0 -1
  1118. package/dist/resource-generation.d.ts.map +0 -1
  1119. package/dist/resource-generation.js.map +0 -1
  1120. package/dist/schema/index.d.ts.map +0 -1
  1121. package/dist/schema/index.js.map +0 -1
  1122. package/dist/schema/meta-loader.d.ts.map +0 -1
  1123. package/dist/schema/meta-loader.js.map +0 -1
  1124. package/dist/schema/resolver.d.ts.map +0 -1
  1125. package/dist/schema/resolver.js.map +0 -1
  1126. package/dist/schema/schema-store-types.d.ts.map +0 -1
  1127. package/dist/schema/schema-store-types.js.map +0 -1
  1128. package/dist/schema/schema-store.d.ts.map +0 -1
  1129. package/dist/schema/schema-store.js.map +0 -1
  1130. package/dist/schema/types.d.ts.map +0 -1
  1131. package/dist/schema/types.js.map +0 -1
  1132. package/dist/server.d.ts.map +0 -1
  1133. package/dist/server.js.map +0 -1
  1134. package/dist/services.d.ts.map +0 -1
  1135. package/dist/services.js.map +0 -1
  1136. package/dist/settings-workspace.d.ts.map +0 -1
  1137. package/dist/settings-workspace.js.map +0 -1
  1138. package/dist/static/index.d.ts.map +0 -1
  1139. package/dist/static/index.js.map +0 -1
  1140. package/dist/static/seo.d.ts.map +0 -1
  1141. package/dist/static/seo.js.map +0 -1
  1142. package/dist/sync-core.d.ts.map +0 -1
  1143. package/dist/sync-core.js.map +0 -1
  1144. package/dist/task-queue/index.d.ts.map +0 -1
  1145. package/dist/task-queue/index.js.map +0 -1
  1146. package/dist/task-queue/task-queue.d.ts.map +0 -1
  1147. package/dist/task-queue/task-queue.js.map +0 -1
  1148. package/dist/task-queue/types.d.ts.map +0 -1
  1149. package/dist/task-queue/types.js.map +0 -1
  1150. package/dist/types.d.ts.map +0 -1
  1151. package/dist/types.js.map +0 -1
  1152. package/dist/url-path-resolver.d.ts.map +0 -1
  1153. package/dist/url-path-resolver.js.map +0 -1
  1154. package/dist/user.d.ts.map +0 -1
  1155. package/dist/user.js.map +0 -1
  1156. package/dist/utils/async-mutex.d.ts.map +0 -1
  1157. package/dist/utils/async-mutex.js.map +0 -1
  1158. package/dist/utils/atomic-write.d.ts.map +0 -1
  1159. package/dist/utils/atomic-write.js.map +0 -1
  1160. package/dist/utils/body-field.d.ts.map +0 -1
  1161. package/dist/utils/body-field.js.map +0 -1
  1162. package/dist/utils/content-write-lock.d.ts.map +0 -1
  1163. package/dist/utils/content-write-lock.js.map +0 -1
  1164. package/dist/utils/debug.d.ts.map +0 -1
  1165. package/dist/utils/debug.js.map +0 -1
  1166. package/dist/utils/entry-url.d.ts.map +0 -1
  1167. package/dist/utils/entry-url.js.map +0 -1
  1168. package/dist/utils/error.d.ts.map +0 -1
  1169. package/dist/utils/error.js.map +0 -1
  1170. package/dist/utils/flatten-group-fields.d.ts.map +0 -1
  1171. package/dist/utils/flatten-group-fields.js.map +0 -1
  1172. package/dist/utils/format.d.ts.map +0 -1
  1173. package/dist/utils/format.js.map +0 -1
  1174. package/dist/utils/fs.d.ts.map +0 -1
  1175. package/dist/utils/fs.js.map +0 -1
  1176. package/dist/utils/git.d.ts.map +0 -1
  1177. package/dist/utils/git.js.map +0 -1
  1178. package/dist/utils/logger.d.ts.map +0 -1
  1179. package/dist/utils/logger.js.map +0 -1
  1180. package/dist/utils/occ-json-write.d.ts.map +0 -1
  1181. package/dist/utils/occ-json-write.js.map +0 -1
  1182. package/dist/utils/provisioning-lock.d.ts.map +0 -1
  1183. package/dist/utils/provisioning-lock.js.map +0 -1
  1184. package/dist/utils/sanitize-href.d.ts.map +0 -1
  1185. package/dist/utils/sanitize-href.js.map +0 -1
  1186. package/dist/utils/title-field.d.ts.map +0 -1
  1187. package/dist/utils/title-field.js.map +0 -1
  1188. package/dist/validation/deletion-checker.d.ts.map +0 -1
  1189. package/dist/validation/deletion-checker.js.map +0 -1
  1190. package/dist/validation/entry-link-validator.d.ts.map +0 -1
  1191. package/dist/validation/entry-link-validator.js.map +0 -1
  1192. package/dist/validation/entry-type-reference-validator.d.ts.map +0 -1
  1193. package/dist/validation/entry-type-reference-validator.js.map +0 -1
  1194. package/dist/validation/entry-validator.d.ts.map +0 -1
  1195. package/dist/validation/entry-validator.js.map +0 -1
  1196. package/dist/validation/field-traversal.d.ts.map +0 -1
  1197. package/dist/validation/field-traversal.js.map +0 -1
  1198. package/dist/validation/reference-validator.d.ts.map +0 -1
  1199. package/dist/validation/reference-validator.js.map +0 -1
  1200. package/dist/worker/cms-worker.d.ts.map +0 -1
  1201. package/dist/worker/cms-worker.js.map +0 -1
  1202. package/dist/worker/log.d.ts.map +0 -1
  1203. package/dist/worker/log.js.map +0 -1
  1204. package/dist/worker/task-queue-config.d.ts.map +0 -1
  1205. package/dist/worker/task-queue-config.js.map +0 -1
  1206. package/dist/worker/task-queue.d.ts.map +0 -1
  1207. package/dist/worker/task-queue.js.map +0 -1
  1208. package/dist/worker/worker-status.d.ts.map +0 -1
  1209. package/dist/worker/worker-status.js.map +0 -1
@@ -8,12 +8,27 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // dist/utils/logger.js
12
+ function canopyLogWarn(...args) {
13
+ active.warn(...args);
14
+ }
15
+ function canopyLogError(...args) {
16
+ active.error(...args);
17
+ }
18
+ var active;
19
+ var init_logger = __esm({
20
+ "dist/utils/logger.js"() {
21
+ "use strict";
22
+ active = console;
23
+ }
24
+ });
25
+
11
26
  // dist/paths/normalize.js
12
- function normalizeFilesystemPath(path20) {
13
- return path20.split(/[\\/]+/).filter(Boolean).join("/");
27
+ function normalizeFilesystemPath(path21) {
28
+ return path21.split(/[\\/]+/).filter(Boolean).join("/");
14
29
  }
15
- function hasTraversalSequence(path20) {
16
- const normalized = normalizeFilesystemPath(path20);
30
+ function hasTraversalSequence(path21) {
31
+ const normalized = normalizeFilesystemPath(path21);
17
32
  return normalized.includes("..");
18
33
  }
19
34
  function createLogicalPath(...segments) {
@@ -23,14 +38,14 @@ function createLogicalPath(...segments) {
23
38
  }
24
39
  return normalized;
25
40
  }
26
- function trimSlashes(path20) {
41
+ function trimSlashes(path21) {
27
42
  let start = 0;
28
- let end = path20.length;
29
- while (start < end && path20[start] === "/")
43
+ let end = path21.length;
44
+ while (start < end && path21[start] === "/")
30
45
  start++;
31
- while (end > start && path20[end - 1] === "/")
46
+ while (end > start && path21[end - 1] === "/")
32
47
  end--;
33
- return path20.slice(start, end);
48
+ return path21.slice(start, end);
34
49
  }
35
50
  var init_normalize = __esm({
36
51
  "dist/paths/normalize.js"() {
@@ -38,21 +53,6 @@ var init_normalize = __esm({
38
53
  }
39
54
  });
40
55
 
41
- // dist/utils/logger.js
42
- function canopyLogWarn(...args) {
43
- active.warn(...args);
44
- }
45
- function canopyLogError(...args) {
46
- active.error(...args);
47
- }
48
- var active;
49
- var init_logger = __esm({
50
- "dist/utils/logger.js"() {
51
- "use strict";
52
- active = console;
53
- }
54
- });
55
-
56
56
  // dist/paths/types.js
57
57
  var ROOT_COLLECTION_ID;
58
58
  var init_types = __esm({
@@ -239,7 +239,12 @@ var init_field = __esm({
239
239
  collections: z2.array(z2.string().min(1)).min(1).optional(),
240
240
  entryTypes: z2.array(z2.string().min(1)).min(1).optional(),
241
241
  displayField: z2.string().min(1).optional(),
242
- options: z2.array(referenceOptionSchema).optional()
242
+ options: z2.array(referenceOptionSchema).optional(),
243
+ // Must be listed here, not just on ReferenceFieldConfig: zod strips unknown keys by
244
+ // default, so a consumer that adopts this schema's parse output would silently delete a
245
+ // runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
246
+ // absent on purpose — it is type-inference-only and stripping it changes no behavior.)
247
+ includeBody: z2.boolean().optional()
243
248
  });
244
249
  fieldHolder = [z2.never()];
245
250
  blockSchema = z2.object({
@@ -349,7 +354,7 @@ var init_media = __esm({
349
354
 
350
355
  // dist/config/schemas/config.js
351
356
  import { z as z5 } from "zod";
352
- var defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE;
357
+ var defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, basePathSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE;
353
358
  var init_config = __esm({
354
359
  "dist/config/schemas/config.js"() {
355
360
  "use strict";
@@ -373,6 +378,9 @@ var init_config = __esm({
373
378
  deployedAsSchema = z5.enum(["static", "server"]).default("server");
374
379
  contentRootSchema = relativePathSchema.default("content");
375
380
  sourceRootSchema = z5.string().min(1).optional();
381
+ basePathSchema = z5.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
382
+ message: "basePath must not be empty or only whitespace"
383
+ }).optional();
376
384
  deploymentNameSchema = z5.string().default("prod");
377
385
  devContentSyncSchema = z5.enum(["off", "warn"]).default("warn");
378
386
  devConfigSchema = z5.object({
@@ -435,6 +443,7 @@ var init_config = __esm({
435
443
  deploymentName: deploymentNameSchema.optional(),
436
444
  contentRoot: contentRootSchema.default("content"),
437
445
  sourceRoot: sourceRootSchema.optional(),
446
+ basePath: basePathSchema,
438
447
  editor: editorConfigSchema.optional(),
439
448
  authPlugin: z5.custom().optional(),
440
449
  entryLinkUrl: z5.custom().optional(),
@@ -672,7 +681,7 @@ var init_config2 = __esm({
672
681
  });
673
682
 
674
683
  // dist/operating-mode/client-unsafe-strategy.js
675
- import path8 from "node:path";
684
+ import path9 from "node:path";
676
685
  function operatingStrategy(mode) {
677
686
  const cached = strategyCache.get(mode);
678
687
  if (cached)
@@ -710,32 +719,32 @@ var init_client_unsafe_strategy = __esm({
710
719
  // - shouldCommit(), shouldPush()
711
720
  // Add client-unsafe methods (use Node.js APIs)
712
721
  getWorkspaceRoot(_sourceRoot) {
713
- return path8.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE);
722
+ return path9.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE);
714
723
  }
715
724
  getContentRoot(contentRoot, sourceRoot) {
716
- return path8.resolve(sourceRoot ?? process.cwd(), contentRoot);
725
+ return path9.resolve(sourceRoot ?? process.cwd(), contentRoot);
717
726
  }
718
727
  getContentBranchesRoot(sourceRoot) {
719
- return path8.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
728
+ return path9.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
720
729
  }
721
730
  getContentBranchRoot(branchName, sourceRoot) {
722
- return path8.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
731
+ return path9.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
723
732
  }
724
733
  getGitExcludePattern() {
725
734
  return ".canopy-meta/";
726
735
  }
727
736
  getPermissionsFilePath(root) {
728
- return path8.join(root, this.getPermissionsFileName());
737
+ return path9.join(root, this.getPermissionsFileName());
729
738
  }
730
739
  getGroupsFilePath(root) {
731
- return path8.join(root, this.getGroupsFileName());
740
+ return path9.join(root, this.getGroupsFileName());
732
741
  }
733
742
  getRemoteUrlConfig() {
734
743
  return {
735
744
  shouldAutoInitLocal: false,
736
745
  defaultRemotePath: "",
737
746
  envVarName: "CANOPYCMS_REMOTE_URL",
738
- autoDetectRemotePath: path8.join(this.getWorkspaceRoot(), "remote.git")
747
+ autoDetectRemotePath: path9.join(this.getWorkspaceRoot(), "remote.git")
739
748
  };
740
749
  }
741
750
  requiresExistingRepo() {
@@ -747,7 +756,7 @@ var init_client_unsafe_strategy = __esm({
747
756
  return `canopycms-settings-${resolveDeploymentName(config, "prod")}`;
748
757
  }
749
758
  getSettingsRoot(sourceRoot) {
750
- return path8.join(this.getWorkspaceRoot(sourceRoot), "settings");
759
+ return path9.join(this.getWorkspaceRoot(sourceRoot), "settings");
751
760
  }
752
761
  usesSeparateSettingsBranch() {
753
762
  return true;
@@ -764,25 +773,25 @@ var init_client_unsafe_strategy = __esm({
764
773
  DevStrategy = class extends DevClientSafeStrategy {
765
774
  // Inherits client-safe methods from DevClientSafeStrategy
766
775
  getWorkspaceRoot(sourceRoot) {
767
- return path8.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
776
+ return path9.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
768
777
  }
769
778
  getContentRoot(contentRoot, sourceRoot) {
770
- return path8.resolve(sourceRoot ?? process.cwd(), contentRoot);
779
+ return path9.resolve(sourceRoot ?? process.cwd(), contentRoot);
771
780
  }
772
781
  getContentBranchesRoot(sourceRoot) {
773
- return path8.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
782
+ return path9.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
774
783
  }
775
784
  getContentBranchRoot(branchName, sourceRoot) {
776
- return path8.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
785
+ return path9.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
777
786
  }
778
787
  getGitExcludePattern() {
779
788
  return ".canopy-meta/";
780
789
  }
781
790
  getPermissionsFilePath(root) {
782
- return path8.join(root, this.getPermissionsFileName());
791
+ return path9.join(root, this.getPermissionsFileName());
783
792
  }
784
793
  getGroupsFilePath(root) {
785
- return path8.join(root, this.getGroupsFileName());
794
+ return path9.join(root, this.getGroupsFileName());
786
795
  }
787
796
  getRemoteUrlConfig() {
788
797
  return {
@@ -800,7 +809,7 @@ var init_client_unsafe_strategy = __esm({
800
809
  return `canopycms-settings-${resolveDeploymentName(config, "local")}`;
801
810
  }
802
811
  getSettingsRoot(sourceRoot) {
803
- return path8.join(this.getWorkspaceRoot(sourceRoot), "settings");
812
+ return path9.join(this.getWorkspaceRoot(sourceRoot), "settings");
804
813
  }
805
814
  usesSeparateSettingsBranch() {
806
815
  return true;
@@ -839,18 +848,38 @@ var init_operating_mode = __esm({
839
848
  });
840
849
 
841
850
  // dist/cli/generate-ai-content.js
842
- import path19 from "node:path";
851
+ import path20 from "node:path";
843
852
  import { createJiti } from "jiti";
844
853
 
845
854
  // dist/build/generate-ai-content.js
846
- import fs16 from "node:fs/promises";
847
- import path18 from "node:path";
855
+ import fs17 from "node:fs/promises";
856
+ import path19 from "node:path";
857
+
858
+ // dist/utils/error.js
859
+ function getErrorMessage(err) {
860
+ if (err instanceof Error) {
861
+ return err.message;
862
+ }
863
+ if (typeof err === "string") {
864
+ return err;
865
+ }
866
+ return String(err);
867
+ }
868
+ function isNodeError(err) {
869
+ return err instanceof Error && "code" in err;
870
+ }
871
+ function isNotFoundError(err) {
872
+ return isNodeError(err) && err.code === "ENOENT";
873
+ }
874
+ function isFileExistsError(err) {
875
+ return isNodeError(err) && err.code === "EEXIST";
876
+ }
848
877
 
849
878
  // dist/content-store.js
850
- import fs9 from "node:fs/promises";
851
- import path10 from "node:path";
852
- import matter from "gray-matter";
853
- import { parse as yamlParse, stringify as yamlStringify } from "yaml";
879
+ import fs10 from "node:fs/promises";
880
+ import path11 from "node:path";
881
+ import matter2 from "gray-matter";
882
+ import { parse as yamlParse } from "yaml";
854
883
 
855
884
  // dist/utils/atomic-write.js
856
885
  import fs from "node:fs/promises";
@@ -869,6 +898,207 @@ async function atomicWriteFile(filePath, content) {
869
898
  }
870
899
  }
871
900
 
901
+ // dist/utils/content-serialize.js
902
+ import matter from "gray-matter";
903
+ import { isCollection, isMap, isNode, isScalar, isSeq, parseDocument, stringify as yamlStringify } from "yaml";
904
+
905
+ // dist/validation/block-structural-keys.js
906
+ var BLOCK_DISCRIMINATOR_KEYS = ["template", "_type"];
907
+ var BLOCK_STRUCTURAL_KEYS = new Set(BLOCK_DISCRIMINATOR_KEYS);
908
+ function isBlockStructuralKey(key) {
909
+ return BLOCK_STRUCTURAL_KEYS.has(key);
910
+ }
911
+
912
+ // dist/utils/content-serialize.js
913
+ function isPlainRecord(value) {
914
+ if (typeof value !== "object" || value === null || Array.isArray(value))
915
+ return false;
916
+ const proto = Object.getPrototypeOf(value);
917
+ return proto === Object.prototype || proto === null;
918
+ }
919
+ function recordKeyOf(keyNode) {
920
+ if (!isScalar(keyNode))
921
+ return void 0;
922
+ const { value } = keyNode;
923
+ if (value === null || value === void 0)
924
+ return void 0;
925
+ if (typeof value === "object")
926
+ return void 0;
927
+ return String(value);
928
+ }
929
+ function carryComments(from, to) {
930
+ if (!isNode(from) || !isNode(to))
931
+ return;
932
+ const source = from;
933
+ const target = to;
934
+ if (source.commentBefore !== void 0)
935
+ target.commentBefore = source.commentBefore;
936
+ if (source.comment !== void 0)
937
+ target.comment = source.comment;
938
+ if (source.spaceBefore !== void 0)
939
+ target.spaceBefore = source.spaceBefore;
940
+ }
941
+ function identityKey(value) {
942
+ try {
943
+ return JSON.stringify(value) ?? "undefined";
944
+ } catch {
945
+ return void 0;
946
+ }
947
+ }
948
+ function nodePlainValue(node) {
949
+ if (!isNode(node))
950
+ return node;
951
+ try {
952
+ return node.toJSON();
953
+ } catch {
954
+ return void 0;
955
+ }
956
+ }
957
+ function nodeIdentityKey(node) {
958
+ const plain = nodePlainValue(node);
959
+ return plain === void 0 && isNode(node) ? void 0 : identityKey(plain);
960
+ }
961
+ var EVIDENCE_MAX_DEPTH = 6;
962
+ function sharesFieldEvidence(existing, value, depth) {
963
+ for (const key of Object.keys(value)) {
964
+ if (isBlockStructuralKey(key))
965
+ continue;
966
+ if (!Object.prototype.hasOwnProperty.call(existing, key))
967
+ continue;
968
+ const before = existing[key];
969
+ const after = value[key];
970
+ if (depth < EVIDENCE_MAX_DEPTH && isPlainRecord(before) && isPlainRecord(after) && sharesFieldEvidence(before, after, depth + 1)) {
971
+ return true;
972
+ }
973
+ const beforeKey = identityKey(before);
974
+ if (beforeKey !== void 0 && beforeKey === identityKey(after))
975
+ return true;
976
+ }
977
+ return false;
978
+ }
979
+ function looksLikeSameItem(node, value) {
980
+ const existing = nodePlainValue(node);
981
+ if (!isPlainRecord(existing) || !isPlainRecord(value))
982
+ return true;
983
+ return sharesFieldEvidence(existing, value, 0);
984
+ }
985
+ function reconcileNode(doc, existing, value) {
986
+ if (isMap(existing) && isPlainRecord(value)) {
987
+ reconcileMap(doc, existing, value);
988
+ return existing;
989
+ }
990
+ if (isSeq(existing) && Array.isArray(value)) {
991
+ reconcileSeq(doc, existing, value);
992
+ return existing;
993
+ }
994
+ if (isScalar(existing) && Object.is(existing.value, value))
995
+ return existing;
996
+ const fresh = doc.createNode(value);
997
+ if (!isCollection(existing))
998
+ carryComments(existing, fresh);
999
+ return fresh;
1000
+ }
1001
+ function reconcileMap(doc, map, value) {
1002
+ const wanted = new Set(Object.keys(value).filter((key) => value[key] !== void 0));
1003
+ const seen = /* @__PURE__ */ new Set();
1004
+ const retained = [];
1005
+ for (const pair of map.items) {
1006
+ const key = recordKeyOf(pair.key);
1007
+ if (key === void 0 || !wanted.has(key) || seen.has(key))
1008
+ continue;
1009
+ seen.add(key);
1010
+ pair.value = reconcileNode(doc, pair.value, value[key]);
1011
+ retained.push(pair);
1012
+ }
1013
+ map.items = retained;
1014
+ for (const key of Object.keys(value)) {
1015
+ if (seen.has(key) || !wanted.has(key))
1016
+ continue;
1017
+ map.set(doc.createNode(key), doc.createNode(value[key]));
1018
+ }
1019
+ }
1020
+ function reconcileSeq(doc, seq, value) {
1021
+ const oldItems = seq.items;
1022
+ const byIdentity = /* @__PURE__ */ new Map();
1023
+ oldItems.forEach((node, index) => {
1024
+ const key = nodeIdentityKey(node);
1025
+ if (key === void 0)
1026
+ return;
1027
+ const bucket = byIdentity.get(key);
1028
+ if (bucket)
1029
+ bucket.push(index);
1030
+ else
1031
+ byIdentity.set(key, [index]);
1032
+ });
1033
+ const consumed = /* @__PURE__ */ new Set();
1034
+ const matches = value.map((item) => {
1035
+ const key = identityKey(item);
1036
+ if (key === void 0)
1037
+ return void 0;
1038
+ const bucket = byIdentity.get(key);
1039
+ while (bucket && bucket.length > 0) {
1040
+ const index = bucket.shift();
1041
+ if (index !== void 0 && !consumed.has(index)) {
1042
+ consumed.add(index);
1043
+ return index;
1044
+ }
1045
+ }
1046
+ return void 0;
1047
+ });
1048
+ seq.items = value.map((item, index) => {
1049
+ const matched = matches[index];
1050
+ if (matched !== void 0)
1051
+ return oldItems[matched];
1052
+ const candidate = index < oldItems.length && !consumed.has(index) ? oldItems[index] : void 0;
1053
+ const sameItem = candidate !== void 0 && looksLikeSameItem(candidate, item) ? candidate : void 0;
1054
+ return reconcileNode(doc, sameItem, item);
1055
+ });
1056
+ }
1057
+ function applyDataToDocument(doc, data) {
1058
+ doc.contents = reconcileNode(doc, doc.contents, data);
1059
+ }
1060
+ function serializeYaml(data, existingRaw) {
1061
+ if (existingRaw === void 0)
1062
+ return yamlStringify(data);
1063
+ const doc = parseDocument(existingRaw);
1064
+ if (doc.errors.length > 0)
1065
+ return yamlStringify(data);
1066
+ applyDataToDocument(doc, data);
1067
+ return doc.toString();
1068
+ }
1069
+ function extractRawFrontmatter(raw) {
1070
+ let parsed;
1071
+ try {
1072
+ parsed = matter(raw, {});
1073
+ } catch {
1074
+ return void 0;
1075
+ }
1076
+ const frontmatter = parsed.matter;
1077
+ if (typeof frontmatter !== "string" || frontmatter.trim() === "")
1078
+ return void 0;
1079
+ return frontmatter;
1080
+ }
1081
+ function serializeFrontmatter(body, data, existingRaw) {
1082
+ if (existingRaw === void 0)
1083
+ return matter.stringify(body, data);
1084
+ const existingFrontmatter = extractRawFrontmatter(existingRaw);
1085
+ if (existingFrontmatter === void 0)
1086
+ return matter.stringify(body, data);
1087
+ const doc = parseDocument(existingFrontmatter);
1088
+ if (doc.errors.length > 0)
1089
+ return matter.stringify(body, data);
1090
+ applyDataToDocument(doc, data);
1091
+ const reconciled = doc.toString();
1092
+ return matter.stringify(body, data, {
1093
+ engines: {
1094
+ yaml: {
1095
+ parse: () => ({}),
1096
+ stringify: () => reconciled
1097
+ }
1098
+ }
1099
+ });
1100
+ }
1101
+
872
1102
  // dist/utils/async-mutex.js
873
1103
  var locks = /* @__PURE__ */ new Map();
874
1104
  async function withLock(key, fn) {
@@ -890,46 +1120,66 @@ async function withLock(key, fn) {
890
1120
 
891
1121
  // dist/utils/content-write-lock.js
892
1122
  import path3 from "node:path";
893
-
894
- // dist/utils/error.js
895
- function getErrorMessage(err) {
896
- if (err instanceof Error) {
897
- return err.message;
898
- }
899
- if (typeof err === "string") {
900
- return err;
901
- }
902
- return String(err);
903
- }
904
- function isNodeError(err) {
905
- return err instanceof Error && "code" in err;
906
- }
907
- function isNotFoundError(err) {
908
- return isNodeError(err) && err.code === "ENOENT";
909
- }
910
- function isFileExistsError(err) {
911
- return isNodeError(err) && err.code === "EEXIST";
912
- }
1123
+ init_logger();
913
1124
 
914
1125
  // dist/utils/provisioning-lock.js
915
1126
  import fs2 from "node:fs/promises";
916
1127
  import path2 from "node:path";
917
1128
  import lockfile from "proper-lockfile";
918
- async function acquireProvisioningLock(lockTargetDir2, lockName) {
1129
+ init_logger();
1130
+ function provisioningLockOptions(lockPath, retries, onCompromised) {
1131
+ return {
1132
+ lockfilePath: lockPath,
1133
+ realpath: false,
1134
+ retries,
1135
+ stale: 3e4,
1136
+ // proper-lockfile invokes this from inside its refresh timer, so ANY throw
1137
+ // escaping here is an uncaught exception that kills the process -- the very
1138
+ // failure the handler exists to prevent. Call sites are told not to throw
1139
+ // (see OnLockCompromised); this makes it structural rather than a
1140
+ // convention, and also covers the LOGGER throwing: under `CI=true`,
1141
+ // vitest's `onConsoleLog` turns any console write into a throw.
1142
+ onCompromised: (err) => {
1143
+ try {
1144
+ if (onCompromised) {
1145
+ onCompromised(err);
1146
+ return;
1147
+ }
1148
+ canopyLogWarn(`[canopy] Provisioning lock compromised mid-hold for ${lockPath}:`, getErrorMessage(err));
1149
+ } catch {
1150
+ try {
1151
+ process.stderr.write(`[canopy] lock compromised for ${lockPath}; its handler or logger threw
1152
+ `);
1153
+ } catch {
1154
+ }
1155
+ }
1156
+ }
1157
+ };
1158
+ }
1159
+ function releaseIgnoringAlreadyReleased(release, lockPath) {
1160
+ return async () => {
1161
+ try {
1162
+ await release();
1163
+ } catch (err) {
1164
+ if (isNodeError(err) && err.code === "ERELEASED") {
1165
+ canopyLogWarn(`[canopy] Lock at ${lockPath} was already released (compromised mid-hold, or released twice); nothing to release`);
1166
+ return;
1167
+ }
1168
+ throw err;
1169
+ }
1170
+ };
1171
+ }
1172
+ async function acquireProvisioningLock(lockTargetDir2, lockName, onCompromised) {
919
1173
  await fs2.mkdir(lockTargetDir2, { recursive: true });
920
- return lockfile.lock(lockTargetDir2, {
921
- lockfilePath: path2.join(lockTargetDir2, lockName),
922
- retries: { retries: 600, factor: 1, minTimeout: 300, maxTimeout: 800, randomize: true },
923
- stale: 3e4
924
- });
1174
+ const lockPath = path2.join(lockTargetDir2, lockName);
1175
+ const release = await lockfile.lock(lockPath, provisioningLockOptions(lockPath, { retries: 600, factor: 1, minTimeout: 300, maxTimeout: 800, randomize: true }, onCompromised));
1176
+ return releaseIgnoringAlreadyReleased(release, lockPath);
925
1177
  }
926
- async function tryAcquireProvisioningLock(lockTargetDir2, lockName) {
1178
+ async function tryAcquireProvisioningLock(lockTargetDir2, lockName, onCompromised) {
927
1179
  await fs2.mkdir(lockTargetDir2, { recursive: true });
928
- return lockfile.lock(lockTargetDir2, {
929
- lockfilePath: path2.join(lockTargetDir2, lockName),
930
- retries: 0,
931
- stale: 3e4
932
- });
1180
+ const lockPath = path2.join(lockTargetDir2, lockName);
1181
+ const release = await lockfile.lock(lockPath, provisioningLockOptions(lockPath, 0, onCompromised));
1182
+ return releaseIgnoringAlreadyReleased(release, lockPath);
933
1183
  }
934
1184
 
935
1185
  // dist/utils/content-write-lock.js
@@ -948,14 +1198,14 @@ function lockTargetDir(branchRoot) {
948
1198
  var sleep = (ms) => new Promise((resolve2) => {
949
1199
  setTimeout(resolve2, ms);
950
1200
  });
951
- function tryAcquireContentWriteLock(branchRoot) {
952
- return tryAcquireProvisioningLock(lockTargetDir(branchRoot), CONTENT_WRITE_LOCK_NAME);
1201
+ function tryAcquireContentWriteLock(branchRoot, onCompromised) {
1202
+ return tryAcquireProvisioningLock(lockTargetDir(branchRoot), CONTENT_WRITE_LOCK_NAME, onCompromised);
953
1203
  }
954
- async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS) {
1204
+ async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, onCompromised) {
955
1205
  const startedAt = Date.now();
956
1206
  for (let attempt = 0; ; attempt++) {
957
1207
  try {
958
- return await tryAcquireContentWriteLock(branchRoot);
1208
+ return await tryAcquireContentWriteLock(branchRoot, onCompromised);
959
1209
  } catch (err) {
960
1210
  if (!isNodeError(err) || err.code !== "ELOCKED")
961
1211
  throw err;
@@ -968,12 +1218,21 @@ async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRIT
968
1218
  }
969
1219
  }
970
1220
  async function withContentWriteLock(branchRoot, fn, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS) {
971
- const release = await acquireContentWriteLock(branchRoot, waitMs);
1221
+ let compromised = false;
1222
+ const release = await acquireContentWriteLock(branchRoot, waitMs, (err) => {
1223
+ compromised = true;
1224
+ canopyLogWarn(`[canopy] Content-write lock compromised mid-hold for ${branchRoot}:`, getErrorMessage(err));
1225
+ });
1226
+ let result;
972
1227
  try {
973
- return await fn();
1228
+ result = await fn();
974
1229
  } finally {
975
1230
  await release();
976
1231
  }
1232
+ if (compromised) {
1233
+ throw new ContentWriteLockBusyError("This branch was being synced while your change was written, so the change may or may not have been recorded. Reload the entry to see the current state before saving again.");
1234
+ }
1235
+ return result;
977
1236
  }
978
1237
 
979
1238
  // dist/utils/flatten-group-fields.js
@@ -998,6 +1257,105 @@ function findBodyFieldName(fields) {
998
1257
  return "body";
999
1258
  }
1000
1259
 
1260
+ // dist/static/seo.js
1261
+ var DEFAULT_SEO_FIELD_NAMES = {
1262
+ title: "metaTitle",
1263
+ description: "metaDescription",
1264
+ ogImage: "ogImage",
1265
+ ogType: "ogType",
1266
+ canonical: "canonical",
1267
+ noindex: "noindex",
1268
+ twitterCard: "twitterCard"
1269
+ };
1270
+
1271
+ // dist/entry-schema.js
1272
+ function buildResolvedReference(data, meta, body) {
1273
+ const resolved = { ...data };
1274
+ if (body && body.value)
1275
+ resolved[body.fieldName] = body.value;
1276
+ resolved.id = meta.id;
1277
+ resolved.slug = meta.slug;
1278
+ resolved.collection = meta.collection;
1279
+ resolved.urlPath = meta.urlPath;
1280
+ return resolved;
1281
+ }
1282
+ var SEO_GROUP_FIELDS = [
1283
+ {
1284
+ name: DEFAULT_SEO_FIELD_NAMES.title,
1285
+ type: "string",
1286
+ label: "Meta Title",
1287
+ description: "Overrides the page title in search results and social cards.",
1288
+ required: false
1289
+ },
1290
+ {
1291
+ name: DEFAULT_SEO_FIELD_NAMES.description,
1292
+ type: "string",
1293
+ label: "Meta Description",
1294
+ description: "Summary shown under the title in search results.",
1295
+ required: false
1296
+ },
1297
+ {
1298
+ name: DEFAULT_SEO_FIELD_NAMES.ogImage,
1299
+ type: "string",
1300
+ label: "Social Image URL",
1301
+ description: "Image for social cards. Site-relative or absolute.",
1302
+ required: false
1303
+ },
1304
+ {
1305
+ name: DEFAULT_SEO_FIELD_NAMES.ogType,
1306
+ type: "select",
1307
+ label: "OpenGraph Type",
1308
+ options: ["website", "article", "profile"],
1309
+ required: false
1310
+ },
1311
+ {
1312
+ name: DEFAULT_SEO_FIELD_NAMES.canonical,
1313
+ type: "string",
1314
+ label: "Canonical URL",
1315
+ description: "Set only to point at a different canonical copy of this page.",
1316
+ required: false
1317
+ },
1318
+ {
1319
+ name: DEFAULT_SEO_FIELD_NAMES.noindex,
1320
+ type: "boolean",
1321
+ label: "Hide from search engines",
1322
+ description: "Marks the page noindex AND drops it from the sitemap.",
1323
+ required: false
1324
+ },
1325
+ {
1326
+ name: DEFAULT_SEO_FIELD_NAMES.twitterCard,
1327
+ type: "select",
1328
+ label: "Twitter Card",
1329
+ options: ["summary", "summary_large_image"],
1330
+ required: false
1331
+ }
1332
+ ];
1333
+
1334
+ // dist/utils/entry-url.js
1335
+ init_normalize();
1336
+ function isIndexSlug(slug) {
1337
+ return slug?.toLowerCase() === "index";
1338
+ }
1339
+ function computeEntryUrl(collection, slug, contentRoot) {
1340
+ const root = trimSlashes(contentRoot);
1341
+ let stripped = collection;
1342
+ if (root && collection.startsWith(`${root}/`)) {
1343
+ stripped = collection.slice(root.length + 1);
1344
+ } else if (collection === root) {
1345
+ stripped = "";
1346
+ }
1347
+ const segments = stripped.split("/").filter(Boolean);
1348
+ if (slug && !isIndexSlug(slug)) {
1349
+ segments.push(slug);
1350
+ }
1351
+ const path21 = segments.length > 0 ? `/${segments.join("/")}` : "/";
1352
+ return path21.toLowerCase();
1353
+ }
1354
+
1355
+ // dist/url-collision.js
1356
+ import fs4 from "node:fs/promises";
1357
+ import path5 from "node:path";
1358
+
1001
1359
  // dist/content-id-index.js
1002
1360
  import fs3 from "node:fs/promises";
1003
1361
  import path4 from "node:path";
@@ -1013,6 +1371,37 @@ var PHYSICAL_SEGMENT_PATTERN = new RegExp(`\\.${BASE58_PATTERN}{12}(?:\\.[a-z]+)
1013
1371
  function isValidContentId(id) {
1014
1372
  return CONTENT_ID_PATTERN.test(id);
1015
1373
  }
1374
+ function parseSlug(slug) {
1375
+ if (!slug) {
1376
+ return {
1377
+ ok: false,
1378
+ error: "Slug is required"
1379
+ };
1380
+ }
1381
+ if (slug.length > 64) {
1382
+ return { ok: false, error: "Slug too long (max 64 characters)" };
1383
+ }
1384
+ if (slug.includes("/") || slug.includes("\\")) {
1385
+ return {
1386
+ ok: false,
1387
+ error: "Slug cannot contain path separators"
1388
+ };
1389
+ }
1390
+ if (slug === "." || slug === "..") {
1391
+ return {
1392
+ ok: false,
1393
+ error: "Slug cannot be a traversal sequence"
1394
+ };
1395
+ }
1396
+ const normalized = slug.toLowerCase();
1397
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(normalized)) {
1398
+ return {
1399
+ ok: false,
1400
+ error: "Slug must start with a letter or number and contain only lowercase letters, numbers, and hyphens"
1401
+ };
1402
+ }
1403
+ return { ok: true, slug: normalized };
1404
+ }
1016
1405
 
1017
1406
  // dist/id.js
1018
1407
  function generateId() {
@@ -1357,13 +1746,13 @@ function extractIdFromFilename(filename) {
1357
1746
  return null;
1358
1747
  }
1359
1748
  async function resolveCollectionPath(root, logicalPath) {
1360
- const fs17 = await import("node:fs/promises");
1361
- const path20 = await import("node:path");
1749
+ const fs18 = await import("node:fs/promises");
1750
+ const path21 = await import("node:path");
1362
1751
  const segments = logicalPath.split("/").filter(Boolean);
1363
1752
  let currentPath = root;
1364
1753
  for (const segment of segments) {
1365
1754
  try {
1366
- const entries = await fs17.readdir(currentPath, { withFileTypes: true });
1755
+ const entries = await fs18.readdir(currentPath, { withFileTypes: true });
1367
1756
  const matchingDir = entries.find((entry) => {
1368
1757
  if (!entry.isDirectory())
1369
1758
  return false;
@@ -1371,7 +1760,7 @@ async function resolveCollectionPath(root, logicalPath) {
1371
1760
  return logicalName === segment.toLowerCase();
1372
1761
  });
1373
1762
  if (matchingDir) {
1374
- currentPath = path20.join(currentPath, matchingDir.name);
1763
+ currentPath = path21.join(currentPath, matchingDir.name);
1375
1764
  } else {
1376
1765
  return null;
1377
1766
  }
@@ -1421,8 +1810,99 @@ function extractSlugFromFilename(filename, entryTypeName) {
1421
1810
  return filename.toLowerCase();
1422
1811
  }
1423
1812
 
1813
+ // dist/utils/typed-filename.js
1814
+ var parseTypedFilename = (filename, entryTypes) => {
1815
+ if (filename.startsWith("."))
1816
+ return null;
1817
+ const lastDot = filename.lastIndexOf(".");
1818
+ if (lastDot === -1)
1819
+ return null;
1820
+ const nameWithoutExt = filename.slice(0, lastDot);
1821
+ const parts = nameWithoutExt.split(".");
1822
+ if (parts.length < 3)
1823
+ return null;
1824
+ const potentialType = parts[0];
1825
+ if (entryTypes && !entryTypes.some((e) => e.name === potentialType)) {
1826
+ return null;
1827
+ }
1828
+ const id = parts[parts.length - 1];
1829
+ if (!isValidId(id))
1830
+ return null;
1831
+ const slug = parts.slice(1, -1).join(".").toLowerCase();
1832
+ return {
1833
+ type: potentialType,
1834
+ slug,
1835
+ id
1836
+ };
1837
+ };
1838
+
1839
+ // dist/url-collision.js
1840
+ var CONTENT_EXTENSIONS = [".md", ".mdx", ".json", ".yaml"];
1841
+ async function readDirSafe(dir) {
1842
+ try {
1843
+ return await fs4.readdir(dir, { withFileTypes: true });
1844
+ } catch (err) {
1845
+ if (isNotFoundError(err))
1846
+ return [];
1847
+ throw err;
1848
+ }
1849
+ }
1850
+ async function findChildCollectionDir(dir, name) {
1851
+ const wanted = name.toLowerCase();
1852
+ for (const entry of await readDirSafe(dir)) {
1853
+ if (!entry.isDirectory())
1854
+ continue;
1855
+ if (extractSlugFromFilename(entry.name) === wanted)
1856
+ return path5.join(dir, entry.name);
1857
+ }
1858
+ return null;
1859
+ }
1860
+ function entrySlugOf(filename) {
1861
+ if (!CONTENT_EXTENSIONS.some((ext) => filename.endsWith(ext)))
1862
+ return null;
1863
+ return parseTypedFilename(filename)?.slug ?? null;
1864
+ }
1865
+ async function findIndexEntryIn(dir) {
1866
+ for (const entry of await readDirSafe(dir)) {
1867
+ if (entry.isDirectory())
1868
+ continue;
1869
+ if (isIndexSlug(entrySlugOf(entry.name) ?? void 0))
1870
+ return path5.join(dir, entry.name);
1871
+ }
1872
+ return null;
1873
+ }
1874
+ async function findEntryBySlugIn(dir, slug) {
1875
+ const wanted = slug.toLowerCase();
1876
+ for (const entry of await readDirSafe(dir)) {
1877
+ if (entry.isDirectory())
1878
+ continue;
1879
+ if (entrySlugOf(entry.name) === wanted)
1880
+ return path5.join(dir, entry.name);
1881
+ }
1882
+ return null;
1883
+ }
1884
+ async function findUrlPathClaimant(opts) {
1885
+ const { collectionDir, slug, contentRoot } = opts;
1886
+ if (isIndexSlug(slug)) {
1887
+ const resolvedRoot = path5.resolve(contentRoot);
1888
+ if (path5.resolve(collectionDir) === resolvedRoot)
1889
+ return null;
1890
+ const parentDir = path5.dirname(collectionDir);
1891
+ const ownName = extractSlugFromFilename(path5.basename(collectionDir));
1892
+ if (isIndexSlug(ownName))
1893
+ return null;
1894
+ const claimant = await findEntryBySlugIn(parentDir, ownName);
1895
+ return claimant ? { kind: "parent-entry", physicalPath: claimant, name: ownName } : null;
1896
+ }
1897
+ const childDir = await findChildCollectionDir(collectionDir, slug);
1898
+ if (!childDir)
1899
+ return null;
1900
+ const indexEntry = await findIndexEntryIn(childDir);
1901
+ return indexEntry ? { kind: "sibling-collection-index", physicalPath: indexEntry, name: slug } : null;
1902
+ }
1903
+
1424
1904
  // dist/content-index-registry.js
1425
- import path5 from "node:path";
1905
+ import path6 from "node:path";
1426
1906
  var registry = /* @__PURE__ */ new Map();
1427
1907
  var finalization = new FinalizationRegistry(({ rootKey, ref }) => {
1428
1908
  const refs = registry.get(rootKey);
@@ -1433,7 +1913,7 @@ var finalization = new FinalizationRegistry(({ rootKey, ref }) => {
1433
1913
  registry.delete(rootKey);
1434
1914
  });
1435
1915
  function registerContentIndexForInvalidation(root, target) {
1436
- const rootKey = path5.resolve(root);
1916
+ const rootKey = path6.resolve(root);
1437
1917
  let refs = registry.get(rootKey);
1438
1918
  if (!refs) {
1439
1919
  refs = /* @__PURE__ */ new Set();
@@ -1444,8 +1924,8 @@ function registerContentIndexForInvalidation(root, target) {
1444
1924
  finalization.register(target, { rootKey, ref });
1445
1925
  }
1446
1926
  function invalidateContentIndexesForRoot(root) {
1447
- const rootKey = path5.resolve(root);
1448
- const prefix = rootKey + path5.sep;
1927
+ const rootKey = path6.resolve(root);
1928
+ const prefix = rootKey + path6.sep;
1449
1929
  for (const [storeRoot, refs] of registry) {
1450
1930
  if (storeRoot !== rootKey && !storeRoot.startsWith(prefix))
1451
1931
  continue;
@@ -1463,8 +1943,8 @@ function invalidateContentIndexesForRoot(root) {
1463
1943
  }
1464
1944
 
1465
1945
  // dist/resource-generation.js
1466
- import fs4 from "node:fs/promises";
1467
- import path6 from "node:path";
1946
+ import fs5 from "node:fs/promises";
1947
+ import path7 from "node:path";
1468
1948
  import { randomUUID } from "node:crypto";
1469
1949
 
1470
1950
  // dist/utils/debug.js
@@ -1573,7 +2053,7 @@ var testLogger = createDebugLogger({
1573
2053
  var log = createDebugLogger({ prefix: "ResourceGeneration" });
1574
2054
  var META_DIR2 = ".canopy-meta";
1575
2055
  function resourceGenerationPath(root, resource) {
1576
- return path6.resolve(root, META_DIR2, `${resource}.generation`);
2056
+ return path7.resolve(root, META_DIR2, `${resource}.generation`);
1577
2057
  }
1578
2058
  async function bumpResourceGeneration(root, resource, options) {
1579
2059
  const token = randomUUID();
@@ -1591,7 +2071,7 @@ async function bumpResourceGeneration(root, resource, options) {
1591
2071
  }
1592
2072
  async function readResourceGeneration(root, resource) {
1593
2073
  try {
1594
- const token = await fs4.readFile(resourceGenerationPath(root, resource), "utf-8");
2074
+ const token = await fs5.readFile(resourceGenerationPath(root, resource), "utf-8");
1595
2075
  return { ok: true, token };
1596
2076
  } catch (err) {
1597
2077
  if (isNodeError(err) && err.code === "ENOENT")
@@ -1607,11 +2087,11 @@ function isGenerationCurrent(snapshotToken, read) {
1607
2087
  }
1608
2088
 
1609
2089
  // dist/branch-schema-cache.js
1610
- import fs6 from "node:fs/promises";
1611
- import path7 from "node:path";
2090
+ import fs7 from "node:fs/promises";
2091
+ import path8 from "node:path";
1612
2092
 
1613
2093
  // dist/schema/meta-loader.js
1614
- import { promises as fs5 } from "fs";
2094
+ import { promises as fs6 } from "fs";
1615
2095
  import { join } from "pathe";
1616
2096
  import { z } from "zod";
1617
2097
  import chokidar from "chokidar";
@@ -1648,7 +2128,7 @@ function stripEmbeddedIdFromName(name) {
1648
2128
  async function scanForCollectionMeta(baseDir, relativePath = "") {
1649
2129
  const collections = [];
1650
2130
  try {
1651
- const entries = await fs5.readdir(baseDir, { withFileTypes: true });
2131
+ const entries = await fs6.readdir(baseDir, { withFileTypes: true });
1652
2132
  for (const entry of entries) {
1653
2133
  if (!entry.isDirectory())
1654
2134
  continue;
@@ -1659,8 +2139,8 @@ async function scanForCollectionMeta(baseDir, relativePath = "") {
1659
2139
  const absolutePath = join(baseDir, folderName);
1660
2140
  const metaPath = join(absolutePath, ".collection.json");
1661
2141
  try {
1662
- await fs5.access(metaPath);
1663
- const content = await fs5.readFile(metaPath, "utf-8");
2142
+ await fs6.access(metaPath);
2143
+ const content = await fs6.readFile(metaPath, "utf-8");
1664
2144
  const parsed = JSON.parse(content);
1665
2145
  const meta = collectionMetaSchema.parse(parsed);
1666
2146
  collections.push({
@@ -1692,7 +2172,7 @@ async function loadCollectionMetaFiles(contentRoot) {
1692
2172
  let root = null;
1693
2173
  const rootMetaPath = join(contentRoot, ".collection.json");
1694
2174
  try {
1695
- await fs5.access(rootMetaPath);
2175
+ await fs6.access(rootMetaPath);
1696
2176
  } catch (err) {
1697
2177
  if (err.code === "ENOENT") {
1698
2178
  } else {
@@ -1700,7 +2180,7 @@ async function loadCollectionMetaFiles(contentRoot) {
1700
2180
  }
1701
2181
  }
1702
2182
  try {
1703
- const content = await fs5.readFile(rootMetaPath, "utf-8");
2183
+ const content = await fs6.readFile(rootMetaPath, "utf-8");
1704
2184
  const parsed = JSON.parse(content);
1705
2185
  root = rootCollectionMetaSchema.parse(parsed);
1706
2186
  } catch (err) {
@@ -1887,16 +2367,16 @@ var SCHEMA_GENERATION_RESOURCE = "schema";
1887
2367
  async function isStaleByMtime(dir, cachedAt) {
1888
2368
  let entries;
1889
2369
  try {
1890
- entries = await fs6.readdir(dir, { recursive: true, encoding: "utf-8" });
2370
+ entries = await fs7.readdir(dir, { recursive: true, encoding: "utf-8" });
1891
2371
  } catch {
1892
2372
  return true;
1893
2373
  }
1894
2374
  for (const entry of entries) {
1895
2375
  if (!entry.endsWith(".collection.json"))
1896
2376
  continue;
1897
- const full = path7.join(dir, entry);
2377
+ const full = path8.join(dir, entry);
1898
2378
  try {
1899
- const stat = await fs6.stat(full);
2379
+ const stat = await fs7.stat(full);
1900
2380
  if (stat.mtimeMs > cachedAt.getTime())
1901
2381
  return true;
1902
2382
  } catch {
@@ -1920,7 +2400,7 @@ var BranchSchemaCache = class {
1920
2400
  * the project root, regardless of which entrypoint produced the cwd branchRoot.
1921
2401
  */
1922
2402
  skipDiskCache(branchRoot) {
1923
- return isBuildMode() || path7.resolve(branchRoot) === path7.resolve(process.cwd());
2403
+ return isBuildMode() || path8.resolve(branchRoot) === path8.resolve(process.cwd());
1924
2404
  }
1925
2405
  /**
1926
2406
  * Get schema for a branch (loads from cache or resolves fresh).
@@ -1946,14 +2426,14 @@ var BranchSchemaCache = class {
1946
2426
  * Load schema from cache or resolve fresh if cache is missing or stale.
1947
2427
  */
1948
2428
  async loadFromCacheOrResolve(branchRoot, entrySchemaRegistry, contentRootName) {
1949
- const contentRoot = path7.join(branchRoot, contentRootName);
2429
+ const contentRoot = path8.join(branchRoot, contentRootName);
1950
2430
  const skipDiskCache = this.skipDiskCache(branchRoot);
1951
2431
  if (!skipDiskCache) {
1952
- const cacheDir = path7.join(branchRoot, ".canopy-meta");
1953
- const cachePath = path7.join(cacheDir, "schema-cache.json");
2432
+ const cacheDir = path8.join(branchRoot, ".canopy-meta");
2433
+ const cachePath = path8.join(cacheDir, "schema-cache.json");
1954
2434
  let cacheData = null;
1955
2435
  try {
1956
- const cacheContent = await fs6.readFile(cachePath, "utf-8");
2436
+ const cacheContent = await fs7.readFile(cachePath, "utf-8");
1957
2437
  cacheData = JSON.parse(cacheContent);
1958
2438
  } catch {
1959
2439
  cacheData = null;
@@ -1990,7 +2470,7 @@ var BranchSchemaCache = class {
1990
2470
  */
1991
2471
  async resolveFreshAndPersist(branchRoot, entrySchemaRegistry, contentRootName, options) {
1992
2472
  const { skipDiskCache } = options;
1993
- const contentRoot = path7.join(branchRoot, contentRootName);
2473
+ const contentRoot = path8.join(branchRoot, contentRootName);
1994
2474
  const read = skipDiskCache ? null : await readResourceGeneration(branchRoot, SCHEMA_GENERATION_RESOURCE);
1995
2475
  const result = await this.resolveFresh(contentRoot, entrySchemaRegistry);
1996
2476
  if (!isValidSchema(result.schema)) {
@@ -2003,9 +2483,9 @@ var BranchSchemaCache = class {
2003
2483
  }
2004
2484
  const flatSchema = flattenSchema(result.schema, contentRootName);
2005
2485
  if (!skipDiskCache) {
2006
- const cacheDir = path7.join(branchRoot, ".canopy-meta");
2007
- const cachePath = path7.join(cacheDir, "schema-cache.json");
2008
- await fs6.unlink(path7.join(cacheDir, "schema-cache.stale")).catch(() => {
2486
+ const cacheDir = path8.join(branchRoot, ".canopy-meta");
2487
+ const cachePath = path8.join(cacheDir, "schema-cache.json");
2488
+ await fs7.unlink(path8.join(cacheDir, "schema-cache.stale")).catch(() => {
2009
2489
  });
2010
2490
  if (read && read.ok) {
2011
2491
  const newCache = {
@@ -2015,13 +2495,13 @@ var BranchSchemaCache = class {
2015
2495
  cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
2016
2496
  generation: read.token
2017
2497
  };
2018
- await fs6.mkdir(cacheDir, { recursive: true });
2019
- const tmpPath = path7.join(cacheDir, `schema-cache.tmp.${Date.now()}.${Math.random()}.json`);
2020
- await fs6.writeFile(tmpPath, JSON.stringify(newCache, null, 2), "utf-8");
2498
+ await fs7.mkdir(cacheDir, { recursive: true });
2499
+ const tmpPath = path8.join(cacheDir, `schema-cache.tmp.${Date.now()}.${Math.random()}.json`);
2500
+ await fs7.writeFile(tmpPath, JSON.stringify(newCache, null, 2), "utf-8");
2021
2501
  try {
2022
- await fs6.rename(tmpPath, cachePath);
2502
+ await fs7.rename(tmpPath, cachePath);
2023
2503
  } catch (err) {
2024
- await fs6.unlink(tmpPath).catch(() => {
2504
+ await fs7.unlink(tmpPath).catch(() => {
2025
2505
  });
2026
2506
  throw err;
2027
2507
  }
@@ -2093,10 +2573,10 @@ async function invalidateBranchContentCaches(root) {
2093
2573
  }
2094
2574
 
2095
2575
  // dist/utils/fs.js
2096
- import fs7 from "node:fs/promises";
2576
+ import fs8 from "node:fs/promises";
2097
2577
  async function filePathExists(filePath) {
2098
2578
  try {
2099
- await fs7.stat(filePath);
2579
+ await fs8.stat(filePath);
2100
2580
  return true;
2101
2581
  } catch (err) {
2102
2582
  if (isNotFoundError(err))
@@ -2104,6 +2584,15 @@ async function filePathExists(filePath) {
2104
2584
  throw err;
2105
2585
  }
2106
2586
  }
2587
+ async function readFileIfExists(filePath) {
2588
+ try {
2589
+ return await fs8.readFile(filePath, "utf8");
2590
+ } catch (err) {
2591
+ if (isNotFoundError(err))
2592
+ return void 0;
2593
+ throw err;
2594
+ }
2595
+ }
2107
2596
 
2108
2597
  // dist/utils/format.js
2109
2598
  var getFormatExtension = (format) => {
@@ -2142,8 +2631,8 @@ function validateAndNormalizePath(root, target) {
2142
2631
 
2143
2632
  // dist/paths/branch.js
2144
2633
  init_operating_mode();
2145
- import fs8 from "node:fs/promises";
2146
- import path9 from "node:path";
2634
+ import fs9 from "node:fs/promises";
2635
+ import path10 from "node:path";
2147
2636
 
2148
2637
  // dist/paths/branch-name.js
2149
2638
  function sanitizeBranchName(branchName) {
@@ -2166,11 +2655,11 @@ function resolveBranchPath(options) {
2166
2655
  const safeBranch = sanitizeBranchName(options.branchName);
2167
2656
  const strategy = operatingStrategy(options.mode);
2168
2657
  const baseRoot = resolveContentBranchesRoot(options.mode, options.basePathOverride);
2169
- const normalizedBase = path9.resolve(baseRoot);
2170
- const baseWithSep = normalizedBase.endsWith(path9.sep) ? normalizedBase : `${normalizedBase}${path9.sep}`;
2658
+ const normalizedBase = path10.resolve(baseRoot);
2659
+ const baseWithSep = normalizedBase.endsWith(path10.sep) ? normalizedBase : `${normalizedBase}${path10.sep}`;
2171
2660
  const branchRoot = strategy.getContentBranchRoot(safeBranch, options.basePathOverride);
2172
2661
  const withinBase = (target) => {
2173
- const resolved = path9.resolve(target);
2662
+ const resolved = path10.resolve(target);
2174
2663
  return resolved === normalizedBase || resolved.startsWith(baseWithSep);
2175
2664
  };
2176
2665
  if (!withinBase(branchRoot)) {
@@ -2180,7 +2669,7 @@ function resolveBranchPath(options) {
2180
2669
  }
2181
2670
  async function ensureBranchRoot(options) {
2182
2671
  const result = resolveBranchPath(options);
2183
- await fs8.mkdir(result.branchRoot, { recursive: true });
2672
+ await fs9.mkdir(result.branchRoot, { recursive: true });
2184
2673
  return result;
2185
2674
  }
2186
2675
 
@@ -2226,6 +2715,13 @@ var DuplicateContentIdError = class extends ContentConflictError {
2226
2715
  this.paths = sorted;
2227
2716
  }
2228
2717
  };
2718
+ var UrlPathConflictError = class extends ContentConflictError {
2719
+ constructor(message, conflictingPath) {
2720
+ super(message);
2721
+ this.name = "UrlPathConflictError";
2722
+ this.conflictingPath = conflictingPath;
2723
+ }
2724
+ };
2229
2725
  function getDefaultEntryType(entries) {
2230
2726
  if (!entries || entries.length === 0)
2231
2727
  return void 0;
@@ -2242,6 +2738,7 @@ function validateSlug(slug) {
2242
2738
  var DEFAULT_INDEX_FRESHNESS_INTERVAL_MS = 1e3;
2243
2739
  var FORCED_REFRESH_MIN_INTERVAL_MS = 5e3;
2244
2740
  var STALE_LOOKUP = Symbol("stale-index-lookup");
2741
+ var createReferenceResolveCache = () => /* @__PURE__ */ new Map();
2245
2742
  var ContentStore = class {
2246
2743
  constructor(root, flatSchema, options = {}) {
2247
2744
  this.indexGeneration = 0;
@@ -2250,7 +2747,7 @@ var ContentStore = class {
2250
2747
  this.lastDiskProbeMs = 0;
2251
2748
  this.lastForcedRefreshMs = 0;
2252
2749
  this.indexBuild = null;
2253
- this.root = path10.resolve(root);
2750
+ this.root = path11.resolve(root);
2254
2751
  this.contentRootName = options.contentRootName || "content";
2255
2752
  this.indexFreshnessIntervalMs = options.indexFreshnessIntervalMs ?? DEFAULT_INDEX_FRESHNESS_INTERVAL_MS;
2256
2753
  this.contentWriteLockWaitMs = options.contentWriteLockWaitMs ?? DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS;
@@ -2398,7 +2895,7 @@ var ContentStore = class {
2398
2895
  async findEntryPathsById(dir, id) {
2399
2896
  let entries;
2400
2897
  try {
2401
- entries = await fs9.readdir(dir, { withFileTypes: true });
2898
+ entries = await fs10.readdir(dir, { withFileTypes: true });
2402
2899
  } catch (err) {
2403
2900
  if (isNodeError(err) && err.code === "ENOENT")
2404
2901
  return [];
@@ -2409,7 +2906,7 @@ var ContentStore = class {
2409
2906
  if (entry.isDirectory())
2410
2907
  continue;
2411
2908
  if (extractIdFromFilename(entry.name) === id) {
2412
- matches.push(path10.relative(this.root, path10.join(dir, entry.name)));
2909
+ matches.push(path11.relative(this.root, path11.join(dir, entry.name)));
2413
2910
  }
2414
2911
  }
2415
2912
  return matches.sort();
@@ -2421,11 +2918,11 @@ var ContentStore = class {
2421
2918
  getSchemaItems() {
2422
2919
  return this.schemaIndex.values();
2423
2920
  }
2424
- assertSchemaItem(path20) {
2425
- const normalized = normalizeFilesystemPath(path20);
2921
+ assertSchemaItem(path21) {
2922
+ const normalized = normalizeFilesystemPath(path21);
2426
2923
  const item = this.schemaIndex.get(normalized);
2427
2924
  if (!item) {
2428
- throw new ContentStoreError(`Unknown schema item: ${path20}`, "NO_SCHEMA_ITEM");
2925
+ throw new ContentStoreError(`Unknown schema item: ${path21}`, "NO_SCHEMA_ITEM");
2429
2926
  }
2430
2927
  return item;
2431
2928
  }
@@ -2492,6 +2989,26 @@ var ContentStore = class {
2492
2989
  return this.createLockKey(schemaItem, slug);
2493
2990
  return prePass.id ? this.idLockKey(prePass.id) : prePass.absolutePath;
2494
2991
  }
2992
+ /**
2993
+ * Refuse a create/rename that would give a second entry a `urlPath` another entry already
2994
+ * holds. See url-collision.ts for the two shapes that count, and the legitimate
2995
+ * landing-page-beside-a-collection shape that deliberately does not.
2996
+ *
2997
+ * @param collectionDir Absolute physical directory the entry will live in.
2998
+ * @param slug The entry's slug, as it will be written.
2999
+ */
3000
+ async assertUrlPathAvailable(collectionDir, slug) {
3001
+ const claimant = await findUrlPathClaimant({
3002
+ collectionDir,
3003
+ slug,
3004
+ contentRoot: path11.resolve(this.root, this.contentRootName)
3005
+ });
3006
+ if (!claimant)
3007
+ return;
3008
+ const relative2 = path11.relative(this.root, claimant.physicalPath);
3009
+ const message = claimant.kind === "sibling-collection-index" ? `An entry with slug "${slug}" would share a URL with the index entry of the "${claimant.name}" collection beside it ("${relative2}"), and only one of them could be served there. Rename this entry, or remove that collection's index entry.` : `An index entry here would share a URL with the "${claimant.name}" entry in the parent collection ("${relative2}"), and only one of them could be served there. Rename that entry, or give this collection's landing page a different slug.`;
3010
+ throw new UrlPathConflictError(message, claimant.physicalPath);
3011
+ }
2495
3012
  /**
2496
3013
  * Build absolute and relative paths with security validation.
2497
3014
  * All entries use the unified filename pattern: {type}.{slug}.{id}.{ext}
@@ -2508,7 +3025,7 @@ var ContentStore = class {
2508
3025
  * @param options.entryTypeName - For collections with multiple entry types, specify which one to use. Defaults to the default entry type.
2509
3026
  */
2510
3027
  async buildPaths(schemaItem, slug, options = {}) {
2511
- const rootWithSep = this.root.endsWith(path10.sep) ? this.root : `${this.root}${path10.sep}`;
3028
+ const rootWithSep = this.root.endsWith(path11.sep) ? this.root : `${this.root}${path11.sep}`;
2512
3029
  if (schemaItem.type === "entry-type") {
2513
3030
  const parentPath = schemaItem.parentPath || "";
2514
3031
  const parentCollection = this.schemaIndex.get(parentPath);
@@ -2541,7 +3058,7 @@ var ContentStore = class {
2541
3058
  const entryTypeName = entryTypeConfig?.name || "entry";
2542
3059
  let collectionRoot = await resolveCollectionPath(this.root, schemaItem.logicalPath);
2543
3060
  if (!collectionRoot) {
2544
- collectionRoot = path10.resolve(this.root, schemaItem.logicalPath);
3061
+ collectionRoot = path11.resolve(this.root, schemaItem.logicalPath);
2545
3062
  }
2546
3063
  if (!collectionRoot.startsWith(rootWithSep)) {
2547
3064
  throw new ContentStoreError("Path traversal detected", "VALIDATION");
@@ -2551,7 +3068,7 @@ var ContentStore = class {
2551
3068
  let existingEntryType;
2552
3069
  let foundExisting = false;
2553
3070
  if (!id) {
2554
- const entries = await fs9.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
3071
+ const entries = await fs10.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
2555
3072
  const existingFile = entries.find((entry) => {
2556
3073
  if (entry.isDirectory())
2557
3074
  return false;
@@ -2577,14 +3094,14 @@ var ContentStore = class {
2577
3094
  }
2578
3095
  filename = `${finalEntryTypeName}.${safeSlug}.${id}${ext}`;
2579
3096
  }
2580
- const resolved = path10.resolve(collectionRoot, filename);
2581
- const collectionRootWithSep = collectionRoot.endsWith(path10.sep) ? collectionRoot : `${collectionRoot}${path10.sep}`;
3097
+ const resolved = path11.resolve(collectionRoot, filename);
3098
+ const collectionRootWithSep = collectionRoot.endsWith(path11.sep) ? collectionRoot : `${collectionRoot}${path11.sep}`;
2582
3099
  if (!resolved.startsWith(collectionRootWithSep)) {
2583
3100
  throw new ContentStoreError("Path traversal detected", "VALIDATION");
2584
3101
  }
2585
3102
  return {
2586
3103
  absolutePath: resolved,
2587
- relativePath: path10.relative(this.root, resolved),
3104
+ relativePath: path11.relative(this.root, resolved),
2588
3105
  id,
2589
3106
  entryTypeName: finalEntryTypeName,
2590
3107
  existed
@@ -2620,8 +3137,8 @@ var ContentStore = class {
2620
3137
  async read(collectionPath, slug = "", options = {}) {
2621
3138
  const schemaItem = this.assertSchemaItem(collectionPath);
2622
3139
  const { absolutePath, relativePath, entryTypeName: resolvedEntryTypeName } = await this.buildPaths(schemaItem, slug);
2623
- const stat = await fs9.stat(absolutePath);
2624
- const raw = await fs9.readFile(absolutePath, "utf8");
3140
+ const stat = await fs10.stat(absolutePath);
3141
+ const raw = await fs10.readFile(absolutePath, "utf8");
2625
3142
  let doc;
2626
3143
  let format;
2627
3144
  let fields;
@@ -2660,7 +3177,7 @@ var ContentStore = class {
2660
3177
  absolutePath
2661
3178
  };
2662
3179
  } else {
2663
- const parsed = matter(raw);
3180
+ const parsed = matter2(raw);
2664
3181
  doc = {
2665
3182
  collection: schemaItem.logicalPath,
2666
3183
  collectionName: schemaItem.name,
@@ -2723,7 +3240,7 @@ var ContentStore = class {
2723
3240
  const indexedPath = guardIndex.findById(id)?.relativePath ?? null;
2724
3241
  if (indexedPath && indexedPath !== relativePath) {
2725
3242
  const quarantined = guardIndex.getDuplicateFor(id);
2726
- const bothOnDisk = await filePathExists(absolutePath) && await filePathExists(path10.join(this.root, indexedPath));
3243
+ const bothOnDisk = await filePathExists(absolutePath) && await filePathExists(path11.join(this.root, indexedPath));
2727
3244
  if (quarantined || bothOnDisk) {
2728
3245
  throw new DuplicateContentIdError(id, [
2729
3246
  indexedPath,
@@ -2734,10 +3251,18 @@ var ContentStore = class {
2734
3251
  }
2735
3252
  }
2736
3253
  }
2737
- await fs9.mkdir(path10.dirname(absolutePath), { recursive: true });
3254
+ if (!await filePathExists(absolutePath)) {
3255
+ const chosenSlug = extractSlugFromFilename(path11.basename(absolutePath));
3256
+ const routable = parseSlug(chosenSlug);
3257
+ if (!routable.ok) {
3258
+ throw new ContentStoreError(`Cannot create entry "${chosenSlug}": ${routable.error}. An entry whose slug is not addressable as a URL segment would build and then 404 on every visit.`, "VALIDATION");
3259
+ }
3260
+ await this.assertUrlPathAvailable(path11.dirname(absolutePath), chosenSlug);
3261
+ }
3262
+ await fs10.mkdir(path11.dirname(absolutePath), { recursive: true });
2738
3263
  if (input.expectedVersion !== void 0) {
2739
3264
  try {
2740
- const existing = await fs9.stat(absolutePath);
3265
+ const existing = await fs10.stat(absolutePath);
2741
3266
  if (input.expectedVersion === null) {
2742
3267
  throw new ContentConflictError("An entry with this slug already exists");
2743
3268
  }
@@ -2754,7 +3279,7 @@ var ContentStore = class {
2754
3279
  }
2755
3280
  }
2756
3281
  if (existingId && !await filePathExists(absolutePath)) {
2757
- const actualRelPaths = await this.findEntryPathsById(path10.dirname(absolutePath), existingId);
3282
+ const actualRelPaths = await this.findEntryPathsById(path11.dirname(absolutePath), existingId);
2758
3283
  if (actualRelPaths.length > 1) {
2759
3284
  throw new DuplicateContentIdError(existingId, actualRelPaths);
2760
3285
  }
@@ -2764,14 +3289,15 @@ var ContentStore = class {
2764
3289
  throw new ContentConflictError();
2765
3290
  }
2766
3291
  }
3292
+ const existingRaw = input.format === "json" ? void 0 : await readFileIfExists(absolutePath);
2767
3293
  let content;
2768
3294
  if (input.format === "json") {
2769
3295
  content = `${JSON.stringify(input.data ?? {}, null, 2)}
2770
3296
  `;
2771
3297
  } else if (input.format === "yaml") {
2772
- content = yamlStringify(input.data ?? {});
3298
+ content = serializeYaml(input.data ?? {}, existingRaw);
2773
3299
  } else {
2774
- content = matter.stringify(input.body, input.data ?? {});
3300
+ content = serializeFrontmatter(input.body, input.data ?? {}, existingRaw);
2775
3301
  }
2776
3302
  await atomicWriteFile(absolutePath, content);
2777
3303
  const liveIndex = this._idIndex;
@@ -2780,7 +3306,7 @@ var ContentStore = class {
2780
3306
  const existing = liveIndex.findById(id);
2781
3307
  if (existing) {
2782
3308
  if (existing.relativePath !== relativePath) {
2783
- staleOldAbsPath = path10.join(this.root, existing.relativePath);
3309
+ staleOldAbsPath = path11.join(this.root, existing.relativePath);
2784
3310
  liveIndex.updatePath(existing.id, relativePath);
2785
3311
  }
2786
3312
  } else {
@@ -2793,13 +3319,13 @@ var ContentStore = class {
2793
3319
  }
2794
3320
  }
2795
3321
  if (staleOldAbsPath) {
2796
- await fs9.unlink(staleOldAbsPath).catch((err) => {
3322
+ await fs10.unlink(staleOldAbsPath).catch((err) => {
2797
3323
  if (!isNodeError(err) || err.code !== "ENOENT")
2798
3324
  throw err;
2799
3325
  });
2800
3326
  }
2801
3327
  await this.recordOwnMutation(liveIndex);
2802
- const afterStat = await fs9.stat(absolutePath);
3328
+ const afterStat = await fs10.stat(absolutePath);
2803
3329
  const base = {
2804
3330
  collection: schemaItem.logicalPath,
2805
3331
  collectionName: schemaItem.name,
@@ -2877,7 +3403,7 @@ var ContentStore = class {
2877
3403
  const schemaItem = this.assertSchemaItem(collectionPath);
2878
3404
  const { absolutePath } = await this.buildPaths(schemaItem, slug);
2879
3405
  try {
2880
- await fs9.access(absolutePath);
3406
+ await fs10.access(absolutePath);
2881
3407
  return true;
2882
3408
  } catch (err) {
2883
3409
  if (isNodeError(err) && err.code === "ENOENT")
@@ -2916,7 +3442,7 @@ var ContentStore = class {
2916
3442
  const collectionRoot = await resolveCollectionPath(this.root, collectionPath);
2917
3443
  if (!collectionRoot)
2918
3444
  return 0;
2919
- const entries = await fs9.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
3445
+ const entries = await fs10.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
2920
3446
  return entries.filter((entry) => !entry.isDirectory() && extractEntryTypeFromFilename(entry.name) === entryTypeName).length;
2921
3447
  }
2922
3448
  /**
@@ -2940,7 +3466,7 @@ var ContentStore = class {
2940
3466
  const collection = this.assertCollection(collectionPath);
2941
3467
  const prePass = await this.buildPaths(collection, slug);
2942
3468
  if (!prePass.existed) {
2943
- await fs9.unlink(prePass.absolutePath);
3469
+ await fs10.unlink(prePass.absolutePath);
2944
3470
  return;
2945
3471
  }
2946
3472
  let lockKey = this.entryLockKey(collection, slug, prePass);
@@ -2956,7 +3482,7 @@ var ContentStore = class {
2956
3482
  return RETRY_KEY;
2957
3483
  }
2958
3484
  const { absolutePath, relativePath } = inLock;
2959
- await fs9.unlink(absolutePath);
3485
+ await fs10.unlink(absolutePath);
2960
3486
  const liveIndex = this._idIndex;
2961
3487
  const id = liveIndex.findByPath(relativePath);
2962
3488
  if (id) {
@@ -2992,6 +3518,10 @@ var ContentStore = class {
2992
3518
  if (currentSlug === safeNewSlug) {
2993
3519
  return { newPath: `${collectionPath}/${currentSlug}` };
2994
3520
  }
3521
+ const routable = parseSlug(safeNewSlug);
3522
+ if (!routable.ok) {
3523
+ throw new ContentStoreError(`Cannot rename to "${safeNewSlug}": ${routable.error}. An entry whose slug is not addressable as a URL segment would build and then 404 on every visit.`, "VALIDATION");
3524
+ }
2995
3525
  const prePass = await this.buildPaths(collection, currentSlug);
2996
3526
  if (!prePass.existed) {
2997
3527
  throw new ContentStoreError(`Entry not found: ${currentSlug}`, "NOT_FOUND");
@@ -3018,7 +3548,7 @@ var ContentStore = class {
3018
3548
  }
3019
3549
  destLockKey = currentDestKey;
3020
3550
  const { absolutePath: currentPath, relativePath: currentRelPath } = inLock;
3021
- const currentFilename = path10.basename(currentPath);
3551
+ const currentFilename = path11.basename(currentPath);
3022
3552
  const parts = currentFilename.split(".");
3023
3553
  if (parts.length < 4) {
3024
3554
  throw new ContentStoreError(`Invalid entry filename format: ${currentFilename}`, "VALIDATION");
@@ -3027,10 +3557,10 @@ var ContentStore = class {
3027
3557
  const contentId = parts[parts.length - 2];
3028
3558
  const ext = `.${parts[parts.length - 1]}`;
3029
3559
  const newFilename = `${entryTypeName}.${safeNewSlug}.${contentId}${ext}`;
3030
- const parentDir = path10.dirname(currentPath);
3031
- const newPath = path10.join(parentDir, newFilename);
3560
+ const parentDir = path11.dirname(currentPath);
3561
+ const newPath = path11.join(parentDir, newFilename);
3032
3562
  try {
3033
- const entries = await fs9.readdir(parentDir, { withFileTypes: true });
3563
+ const entries = await fs10.readdir(parentDir, { withFileTypes: true });
3034
3564
  for (const entry of entries) {
3035
3565
  if (entry.isDirectory())
3036
3566
  continue;
@@ -3043,16 +3573,17 @@ var ContentStore = class {
3043
3573
  if (err instanceof ContentStoreError)
3044
3574
  throw err;
3045
3575
  }
3576
+ await this.assertUrlPathAvailable(parentDir, safeNewSlug);
3046
3577
  try {
3047
- await fs9.link(currentPath, newPath);
3578
+ await fs10.link(currentPath, newPath);
3048
3579
  } catch (err) {
3049
3580
  if (isNodeError(err) && err.code === "EEXIST") {
3050
3581
  throw new ContentStoreError(`Entry with slug "${safeNewSlug}" already exists in collection "${collectionPath}"`, "VALIDATION");
3051
3582
  }
3052
3583
  throw err;
3053
3584
  }
3054
- await fs9.unlink(currentPath);
3055
- const newRelativePath = path10.relative(this.root, newPath);
3585
+ await fs10.unlink(currentPath);
3586
+ const newRelativePath = path11.relative(this.root, newPath);
3056
3587
  const liveIndex = this._idIndex;
3057
3588
  const entryId = liveIndex.findByPath(currentRelPath);
3058
3589
  if (entryId) {
@@ -3124,34 +3655,57 @@ var ContentStore = class {
3124
3655
  }
3125
3656
  return entries;
3126
3657
  }
3658
+ /**
3659
+ * Resolve every `reference` field in `data` against `fields`, returning a copy.
3660
+ *
3661
+ * This is what `read()` applies automatically (unless `resolveReferences: false`), exposed
3662
+ * so BATCH surfaces can opt into the same resolution without duplicating the walk: the
3663
+ * listing primitives in content-listing.ts and content-tree.ts read entry files straight
3664
+ * off disk and never touch this store, so before this existed a `reference` field reached
3665
+ * `listEntries()`/`buildContentTree()` callers as a bare id string or `null`.
3666
+ *
3667
+ * Pass one {@link ReferenceResolveCache} across a whole batch so a target referenced by
3668
+ * many entries is read once rather than once per referencing entry. Omit `cache` and the
3669
+ * behavior is exactly what `read()` has always done, unmemoized.
3670
+ *
3671
+ * **Path ACLs are not consulted for the targets.** Resolution goes through this store's
3672
+ * own `read()`, below the per-entry permission check in content-reader.ts — so a resolved
3673
+ * target may be an entry the caller could not `read()` directly. That is pre-existing
3674
+ * `read()` behavior, matched here on purpose so one rule covers both; see the
3675
+ * `resolveReferences` option in content-listing.ts for the note adopters see.
3676
+ */
3677
+ async resolveReferences(data, fields, cache) {
3678
+ return this.resolveReferencesInData(data, fields, cache);
3679
+ }
3127
3680
  /**
3128
3681
  * Recursively resolve reference fields in data.
3129
3682
  * This traverses objects, arrays, and blocks to find and resolve all reference fields.
3130
3683
  */
3131
- async resolveReferencesInData(data, fields) {
3684
+ async resolveReferencesInData(data, fields, cache) {
3132
3685
  const resolved = { ...data };
3133
3686
  const idIndex = await this.idIndex();
3134
3687
  for (const field of fields) {
3135
3688
  if (field.type === "group") {
3136
- const groupResolved = await this.resolveReferencesInData(resolved, field.fields);
3689
+ const groupResolved = await this.resolveReferencesInData(resolved, field.fields, cache);
3137
3690
  Object.assign(resolved, groupResolved);
3138
3691
  continue;
3139
3692
  }
3140
3693
  const value = data[field.name];
3141
3694
  if (field.type === "reference") {
3695
+ const includeBody = field.includeBody === true;
3142
3696
  if (typeof value === "string" && value) {
3143
- resolved[field.name] = await this.resolveSingleReference(value, idIndex);
3697
+ resolved[field.name] = await this.resolveSingleReference(value, idIndex, includeBody, cache);
3144
3698
  } else if (field.list && Array.isArray(value)) {
3145
- resolved[field.name] = await Promise.all(value.map((id) => typeof id === "string" ? this.resolveSingleReference(id, idIndex) : null));
3699
+ resolved[field.name] = await Promise.all(value.map((id) => typeof id === "string" ? this.resolveSingleReference(id, idIndex, includeBody, cache) : null));
3146
3700
  }
3147
3701
  } else if (field.type === "object" && value) {
3148
3702
  const objectField = field;
3149
3703
  if (!objectField.fields)
3150
3704
  continue;
3151
3705
  if (objectField.list && Array.isArray(value)) {
3152
- resolved[field.name] = await Promise.all(value.map((item) => typeof item === "object" && item !== null ? this.resolveReferencesInData(item, objectField.fields) : item));
3706
+ resolved[field.name] = await Promise.all(value.map((item) => typeof item === "object" && item !== null ? this.resolveReferencesInData(item, objectField.fields, cache) : item));
3153
3707
  } else if (typeof value === "object") {
3154
- resolved[field.name] = await this.resolveReferencesInData(value, objectField.fields);
3708
+ resolved[field.name] = await this.resolveReferencesInData(value, objectField.fields, cache);
3155
3709
  }
3156
3710
  } else if (field.type === "block" && Array.isArray(value)) {
3157
3711
  const blockField = field;
@@ -3164,7 +3718,7 @@ var ContentStore = class {
3164
3718
  return block;
3165
3719
  return {
3166
3720
  ...b,
3167
- value: await this.resolveReferencesInData(b.value, template.fields)
3721
+ value: await this.resolveReferencesInData(b.value, template.fields, cache)
3168
3722
  };
3169
3723
  }));
3170
3724
  }
@@ -3172,7 +3726,56 @@ var ContentStore = class {
3172
3726
  return resolved;
3173
3727
  }
3174
3728
  /**
3175
- * Resolve a single reference ID to full entry data.
3729
+ * Resolve a single reference ID to full entry data, memoized when a batch cache is
3730
+ * supplied. Without a cache this is a straight pass-through to the uncached path, so
3731
+ * `read()` behaves exactly as it always has.
3732
+ *
3733
+ * Every occurrence gets its OWN deep copy, even on a cache hit. Without that, the memo
3734
+ * would hand one shared object to all 40 entries referencing the same block — a mutation
3735
+ * in one caller's `extract` (truncating a body for a search index, deleting a field) would
3736
+ * silently rewrite it for every sibling, and both `list: true` elements of `[id, id]` would
3737
+ * be the same instance. The copy is what keeps the cache a pure performance optimization
3738
+ * instead of a semantic change.
3739
+ *
3740
+ * Note the uncached path is NOT the clean baseline it looks like. For a json/yaml target it
3741
+ * genuinely reparses fresh per occurrence, but for md/mdx gray-matter serves `data` from a
3742
+ * process-global content-keyed cache, so `resolveSingleReferenceOnce`'s top-level spread
3743
+ * severs exactly one level and NESTED frontmatter objects alias across occurrences, calls and
3744
+ * requests. That is pre-existing `read()` behavior and out of scope here — deliberately, since
3745
+ * touching it would change `read()` — but it means the cached path is the safer of the two,
3746
+ * not a relaxation of a guarantee the uncached one provides. See
3747
+ * `.claude/future-tasks/graymatter-cache-shared-frontmatter.md`.
3748
+ *
3749
+ * What the copy costs, measured rather than assumed (2000 occurrences of one target, local
3750
+ * disk, so every read hits the page cache — the friendliest possible case for NOT caching):
3751
+ * a snippet-sized target is ~63x cheaper to clone than to re-read-and-parse, while a 265KB
3752
+ * JSON target is ~0.8x, i.e. cloning is marginally SLOWER than reparsing it. So the win is
3753
+ * large in the case this exists for and roughly a wash at the pathological end, never a
3754
+ * blow-up. Two things keep the bad end narrow: an md/mdx target resolves to its FRONTMATTER
3755
+ * only *unless the field sets `includeBody`* (`read()` puts the body on `doc.body`, which
3756
+ * `resolveSingleReferenceOnce` spreads in only for an embedding field), so by default body
3757
+ * size is irrelevant no matter how long the document and only a genuinely huge JSON/YAML
3758
+ * target reaches the wash. **`includeBody: true` is the case that CAN reach it on markdown**:
3759
+ * the body then sits inside the memoized object and is cloned once per referencing entry, so
3760
+ * a long document embedded by many pages pays that repeatedly — the reason `includeBody`
3761
+ * exists as an opt-in per field rather than as resolution's default. And in the deployment this
3762
+ * targets, content lives on EFS/NFS where the syscall the memo removes dominates parse and
3763
+ * clone alike, which the local-disk numbers above understate badly. Correctness is the
3764
+ * reason for the copy regardless; the numbers are here so nobody has to re-derive them
3765
+ * before touching this.
3766
+ */
3767
+ resolveSingleReference(id, idIndex, includeBody, cache) {
3768
+ if (!cache)
3769
+ return this.resolveSingleReferenceUncached(id, idIndex, includeBody);
3770
+ const key = includeBody ? `${id}:body` : id;
3771
+ let pending = cache.get(key);
3772
+ if (!pending) {
3773
+ pending = this.resolveSingleReferenceUncached(id, idIndex, includeBody);
3774
+ cache.set(key, pending);
3775
+ }
3776
+ return pending.then((resolved) => resolved === null ? null : structuredClone(resolved));
3777
+ }
3778
+ /**
3176
3779
  * Returns null if the reference is invalid or missing.
3177
3780
  * Includes id, slug, and collection fields for debugging.
3178
3781
  *
@@ -3186,16 +3789,21 @@ var ContentStore = class {
3186
3789
  * idIndex() dedupes concurrent builds — so a sibling call that wins the
3187
3790
  * throttle and rebuilds heals every other miss in the same batch, not just
3188
3791
  * the first.
3792
+ *
3793
+ * A {@link ReferenceResolveCache} sits ABOVE this, never inside it, so every
3794
+ * distinct id still runs the full refresh-and-retry above. What a cache changes
3795
+ * is only that repeats of the SAME id in one batch share that one attempt's
3796
+ * outcome instead of each getting an independent throw of the dice.
3189
3797
  */
3190
- async resolveSingleReference(id, idIndex) {
3191
- const first = await this.resolveSingleReferenceOnce(id, idIndex);
3798
+ async resolveSingleReferenceUncached(id, idIndex, includeBody) {
3799
+ const first = await this.resolveSingleReferenceOnce(id, idIndex, includeBody);
3192
3800
  if (first !== STALE_LOOKUP)
3193
3801
  return first;
3194
3802
  await this.refreshIndexForSuspiciousLookup();
3195
- const second = await this.resolveSingleReferenceOnce(id, await this.idIndex());
3803
+ const second = await this.resolveSingleReferenceOnce(id, await this.idIndex(), includeBody);
3196
3804
  return second === STALE_LOOKUP ? null : second;
3197
3805
  }
3198
- async resolveSingleReferenceOnce(id, idIndex) {
3806
+ async resolveSingleReferenceOnce(id, idIndex, includeBody) {
3199
3807
  try {
3200
3808
  const location = idIndex.findById(id);
3201
3809
  if (!location)
@@ -3206,12 +3814,13 @@ var ContentStore = class {
3206
3814
  const doc = await this.read(location.collection, location.slug, {
3207
3815
  resolveReferences: false
3208
3816
  });
3209
- return {
3817
+ const bodyForEmbed = includeBody && "body" in doc ? { fieldName: doc.bodyFieldName, value: doc.body } : void 0;
3818
+ return buildResolvedReference(doc.data, {
3210
3819
  id,
3211
3820
  slug: location.slug,
3212
3821
  collection: location.collection,
3213
- ...doc.data
3214
- };
3822
+ urlPath: computeEntryUrl(location.collection, location.slug, this.contentRootName)
3823
+ }, bodyForEmbed);
3215
3824
  } catch (error) {
3216
3825
  if (isNodeError(error) && error.code === "ENOENT")
3217
3826
  return STALE_LOOKUP;
@@ -3222,8 +3831,8 @@ var ContentStore = class {
3222
3831
  };
3223
3832
 
3224
3833
  // dist/ai/generate.js
3225
- import path11 from "node:path";
3226
- import fs10 from "node:fs/promises";
3834
+ import path12 from "node:path";
3835
+ import fs11 from "node:fs/promises";
3227
3836
  import { minimatch } from "minimatch";
3228
3837
 
3229
3838
  // dist/ai/strip-mdx.js
@@ -3505,7 +4114,7 @@ function renderField(field, value, depth, entry, config) {
3505
4114
  const heading = "#".repeat(Math.min(depth, 6));
3506
4115
  const descriptionLine = "description" in field && field.description ? `
3507
4116
 
3508
- *${field.description}*` : "";
4117
+ _${field.description}_` : "";
3509
4118
  if ("list" in field && field.list && Array.isArray(value)) {
3510
4119
  return renderListField(field, value, depth, label, heading, descriptionLine, entry, config);
3511
4120
  }
@@ -3770,30 +4379,14 @@ function formatInlineValue(field, value) {
3770
4379
  return String(value);
3771
4380
  }
3772
4381
  function yamlValue(value) {
4382
+ if (value === "")
4383
+ return '""';
3773
4384
  if (/[:#{}[\],&*?|>!%@`]/.test(value) || value.includes("\n")) {
3774
4385
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
3775
4386
  }
3776
4387
  return value;
3777
4388
  }
3778
4389
 
3779
- // dist/utils/entry-url.js
3780
- init_normalize();
3781
- function computeEntryUrl(collection, slug, contentRoot) {
3782
- const root = trimSlashes(contentRoot);
3783
- let stripped = collection;
3784
- if (root && collection.startsWith(`${root}/`)) {
3785
- stripped = collection.slice(root.length + 1);
3786
- } else if (collection === root) {
3787
- stripped = "";
3788
- }
3789
- const segments = stripped.split("/").filter(Boolean);
3790
- if (slug && slug !== "index") {
3791
- segments.push(slug);
3792
- }
3793
- const path20 = segments.length > 0 ? `/${segments.join("/")}` : "/";
3794
- return path20.toLowerCase();
3795
- }
3796
-
3797
4390
  // dist/entry-link-resolver.js
3798
4391
  var log2 = createDebugLogger({ prefix: "EntryLinks" });
3799
4392
  var BASE58_CHAR = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]";
@@ -3966,7 +4559,7 @@ async function processCollection(store, collection, flatSchema, contentRoot, con
3966
4559
  const listed = await store.getCollectionEntryPaths(collection.logicalPath);
3967
4560
  const directEntries = listed.filter((e) => e.collection === collection.logicalPath);
3968
4561
  for (const listEntry of directEntries) {
3969
- const entryTypeName = extractEntryTypeFromFilename(path11.basename(listEntry.relativePath));
4562
+ const entryTypeName = extractEntryTypeFromFilename(path12.basename(listEntry.relativePath));
3970
4563
  if (!entryTypeName)
3971
4564
  continue;
3972
4565
  if (config?.exclude?.entryTypes?.includes(entryTypeName))
@@ -3984,7 +4577,7 @@ async function processCollection(store, collection, flatSchema, contentRoot, con
3984
4577
  }
3985
4578
  if (config?.exclude?.where?.(aiEntry))
3986
4579
  continue;
3987
- await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path11.basename(listEntry.relativePath)), config);
4580
+ await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path12.basename(listEntry.relativePath)), config);
3988
4581
  entries.push(aiEntry);
3989
4582
  const entryFilePath = `${cleanPath}/${listEntry.slug}.md`;
3990
4583
  const entryMarkdown = entryToMarkdown(aiEntry, config);
@@ -4035,7 +4628,7 @@ async function processRootEntries(store, rootCollection, contentRoot, config, id
4035
4628
  const listed = await store.getCollectionEntryPaths(rootCollection.logicalPath);
4036
4629
  const directEntries = listed.filter((e) => e.collection === rootCollection.logicalPath);
4037
4630
  for (const listEntry of directEntries) {
4038
- const entryTypeName = extractEntryTypeFromFilename(path11.basename(listEntry.relativePath));
4631
+ const entryTypeName = extractEntryTypeFromFilename(path12.basename(listEntry.relativePath));
4039
4632
  if (!entryTypeName)
4040
4633
  continue;
4041
4634
  if (config?.exclude?.entryTypes?.includes(entryTypeName))
@@ -4053,7 +4646,7 @@ async function processRootEntries(store, rootCollection, contentRoot, config, id
4053
4646
  }
4054
4647
  if (config?.exclude?.where?.(aiEntry))
4055
4648
  continue;
4056
- await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path11.basename(listEntry.relativePath)), config);
4649
+ await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path12.basename(listEntry.relativePath)), config);
4057
4650
  entries.push(aiEntry);
4058
4651
  const entryFilePath = `${listEntry.slug}.md`;
4059
4652
  const entryMarkdown = entryToMarkdown(aiEntry, config);
@@ -4101,19 +4694,19 @@ function docToAIEntry(doc, slug, entryTypeName, entryTypeConfig, cleanCollection
4101
4694
  };
4102
4695
  }
4103
4696
  function makeReadSibling(dir) {
4104
- const resolvedDir = path11.resolve(dir);
4697
+ const resolvedDir = path12.resolve(dir);
4105
4698
  return async (name) => {
4106
- if (!name || name.includes("/") || name.includes("\\") || path11.isAbsolute(name) || hasTraversalSequence(name)) {
4699
+ if (!name || name.includes("/") || name.includes("\\") || path12.isAbsolute(name) || hasTraversalSequence(name)) {
4107
4700
  return null;
4108
4701
  }
4109
- const abs = path11.resolve(resolvedDir, name);
4110
- if (path11.dirname(abs) !== resolvedDir)
4702
+ const abs = path12.resolve(resolvedDir, name);
4703
+ if (path12.dirname(abs) !== resolvedDir)
4111
4704
  return null;
4112
4705
  try {
4113
- const stats = await fs10.lstat(abs);
4706
+ const stats = await fs11.lstat(abs);
4114
4707
  if (!stats.isFile())
4115
4708
  return null;
4116
- return await fs10.readFile(abs, "utf8");
4709
+ return await fs11.readFile(abs, "utf8");
4117
4710
  } catch (err) {
4118
4711
  if (isNodeError(err) && (err.code === "ENOENT" || err.code === "ENOTDIR"))
4119
4712
  return null;
@@ -4127,7 +4720,7 @@ async function runEntryTransform(entry, absolutePath, contentId, config) {
4127
4720
  return;
4128
4721
  const ctx = {
4129
4722
  contentId: contentId ?? "",
4130
- readSibling: makeReadSibling(path11.dirname(absolutePath))
4723
+ readSibling: makeReadSibling(path12.dirname(absolutePath))
4131
4724
  };
4132
4725
  try {
4133
4726
  const appended = await transform(entry, ctx);
@@ -4164,15 +4757,15 @@ function matchesBundleFilter(entry, filter, contentRoot) {
4164
4757
  }
4165
4758
 
4166
4759
  // dist/branch-workspace.js
4167
- import path16 from "node:path";
4760
+ import path17 from "node:path";
4168
4761
 
4169
4762
  // dist/branch-metadata.js
4170
- import fs13 from "node:fs/promises";
4171
- import path14 from "node:path";
4763
+ import fs14 from "node:fs/promises";
4764
+ import path15 from "node:path";
4172
4765
 
4173
4766
  // dist/branch-registry.js
4174
- import fs11 from "node:fs/promises";
4175
- import path12 from "node:path";
4767
+ import fs12 from "node:fs/promises";
4768
+ import path13 from "node:path";
4176
4769
  init_logger();
4177
4770
  var log3 = createDebugLogger({ prefix: "BranchRegistry" });
4178
4771
  var REGISTRY_FILE = "branches.json";
@@ -4185,10 +4778,10 @@ var BranchRegistry = class {
4185
4778
  constructor(root) {
4186
4779
  this.regenInFlight = null;
4187
4780
  this.lastForcedRefreshMs = 0;
4188
- this.root = path12.resolve(root);
4189
- this.registryPath = path12.join(this.root, REGISTRY_FILE);
4190
- this.stalePath = path12.join(this.root, REGISTRY_STALE_FILE);
4191
- this.tempPath = path12.join(this.root, REGISTRY_TEMP_FILE);
4781
+ this.root = path13.resolve(root);
4782
+ this.registryPath = path13.join(this.root, REGISTRY_FILE);
4783
+ this.stalePath = path13.join(this.root, REGISTRY_STALE_FILE);
4784
+ this.tempPath = path13.join(this.root, REGISTRY_TEMP_FILE);
4192
4785
  }
4193
4786
  /**
4194
4787
  * Returns all branches. Uses the cached snapshot if its embedded generation
@@ -4197,7 +4790,7 @@ var BranchRegistry = class {
4197
4790
  async list() {
4198
4791
  let parsed;
4199
4792
  try {
4200
- const raw = await fs11.readFile(this.registryPath, "utf8");
4793
+ const raw = await fs12.readFile(this.registryPath, "utf8");
4201
4794
  parsed = JSON.parse(raw);
4202
4795
  } catch (err) {
4203
4796
  if (isNotFoundError(err)) {
@@ -4286,7 +4879,7 @@ var BranchRegistry = class {
4286
4879
  async performRegenerate() {
4287
4880
  const read = await readResourceGeneration(this.root, RESOURCE2);
4288
4881
  const branches = await this.scanBranchDirectories();
4289
- await fs11.unlink(this.stalePath).catch(() => {
4882
+ await fs12.unlink(this.stalePath).catch(() => {
4290
4883
  });
4291
4884
  if (!read.ok) {
4292
4885
  return branches;
@@ -4297,12 +4890,12 @@ var BranchRegistry = class {
4297
4890
  generation: read.token
4298
4891
  };
4299
4892
  const uniqueTempPath = `${this.tempPath}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
4300
- await fs11.mkdir(this.root, { recursive: true });
4301
- await fs11.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
4893
+ await fs12.mkdir(this.root, { recursive: true });
4894
+ await fs12.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
4302
4895
  try {
4303
- await fs11.rename(uniqueTempPath, this.registryPath);
4896
+ await fs12.rename(uniqueTempPath, this.registryPath);
4304
4897
  } catch (err) {
4305
- await fs11.unlink(uniqueTempPath).catch(() => {
4898
+ await fs12.unlink(uniqueTempPath).catch(() => {
4306
4899
  });
4307
4900
  throw err;
4308
4901
  }
@@ -4316,12 +4909,12 @@ var BranchRegistry = class {
4316
4909
  async scanBranchDirectories() {
4317
4910
  const branches = [];
4318
4911
  try {
4319
- const entries = await fs11.readdir(this.root, { withFileTypes: true });
4912
+ const entries = await fs12.readdir(this.root, { withFileTypes: true });
4320
4913
  for (const entry of entries) {
4321
4914
  if (!entry.isDirectory() || entry.name.startsWith(".")) {
4322
4915
  continue;
4323
4916
  }
4324
- const branchRoot = path12.join(this.root, entry.name);
4917
+ const branchRoot = path13.join(this.root, entry.name);
4325
4918
  let meta;
4326
4919
  try {
4327
4920
  meta = await BranchMetadataFileManager.loadOnly(branchRoot);
@@ -4348,8 +4941,8 @@ var BranchRegistry = class {
4348
4941
  };
4349
4942
 
4350
4943
  // dist/utils/occ-json-write.js
4351
- import fs12 from "node:fs/promises";
4352
- import path13 from "node:path";
4944
+ import fs13 from "node:fs/promises";
4945
+ import path14 from "node:path";
4353
4946
  import { randomUUID as randomUUID2 } from "node:crypto";
4354
4947
  import lockfile2 from "proper-lockfile";
4355
4948
  var log4 = createDebugLogger({ prefix: "OccJsonWrite" });
@@ -4365,29 +4958,29 @@ async function writeOccJsonFile(filePath, payload, options) {
4365
4958
  const writeId = randomUUID2();
4366
4959
  const data = { ...payload, version: newVersion, writeId };
4367
4960
  const content = JSON.stringify(data, null, 2) + (trailingNewline ? "\n" : "");
4368
- const dir = path13.dirname(filePath);
4369
- await fs12.mkdir(dir, { recursive: true });
4961
+ const dir = path14.dirname(filePath);
4962
+ await fs13.mkdir(dir, { recursive: true });
4370
4963
  const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
4371
- await fs12.writeFile(tempPath, content, "utf-8");
4964
+ await fs13.writeFile(tempPath, content, "utf-8");
4372
4965
  if (expectedVersion === null) {
4373
4966
  try {
4374
- await fs12.link(tempPath, filePath);
4967
+ await fs13.link(tempPath, filePath);
4375
4968
  } catch (err) {
4376
- await fs12.unlink(tempPath).catch(() => {
4969
+ await fs13.unlink(tempPath).catch(() => {
4377
4970
  });
4378
4971
  if (isFileExistsError(err)) {
4379
4972
  throw new OccWriteConflictError();
4380
4973
  }
4381
4974
  throw err;
4382
4975
  }
4383
- await fs12.unlink(tempPath).catch(() => {
4976
+ await fs13.unlink(tempPath).catch(() => {
4384
4977
  });
4385
4978
  return { version: newVersion, writeId };
4386
4979
  }
4387
4980
  try {
4388
4981
  let currentVersion = null;
4389
4982
  try {
4390
- const current = JSON.parse(await fs12.readFile(filePath, "utf-8"));
4983
+ const current = JSON.parse(await fs13.readFile(filePath, "utf-8"));
4391
4984
  currentVersion = current.version ?? 0;
4392
4985
  } catch {
4393
4986
  currentVersion = null;
@@ -4395,16 +4988,16 @@ async function writeOccJsonFile(filePath, payload, options) {
4395
4988
  if (currentVersion !== expectedVersion) {
4396
4989
  throw new OccWriteConflictError();
4397
4990
  }
4398
- await fs12.rename(tempPath, filePath);
4991
+ await fs13.rename(tempPath, filePath);
4399
4992
  if (settleMs > 0) {
4400
4993
  await new Promise((resolve2) => setTimeout(resolve2, settleMs));
4401
4994
  }
4402
- const afterWrite = JSON.parse(await fs12.readFile(filePath, "utf-8"));
4995
+ const afterWrite = JSON.parse(await fs13.readFile(filePath, "utf-8"));
4403
4996
  if (afterWrite.writeId !== writeId) {
4404
4997
  throw new OccWriteConflictError();
4405
4998
  }
4406
4999
  } catch (err) {
4407
- await fs12.unlink(tempPath).catch(() => {
5000
+ await fs13.unlink(tempPath).catch(() => {
4408
5001
  });
4409
5002
  throw err;
4410
5003
  }
@@ -4430,8 +5023,8 @@ async function withOccRetry(operation, options) {
4430
5023
  throw new Error("Unreachable");
4431
5024
  }
4432
5025
  async function withOccFileLock(filePath, fn) {
4433
- const dir = path13.dirname(filePath);
4434
- await fs12.mkdir(dir, { recursive: true });
5026
+ const dir = path14.dirname(filePath);
5027
+ await fs13.mkdir(dir, { recursive: true });
4435
5028
  const MAX_ATTEMPTS = 14;
4436
5029
  let release = null;
4437
5030
  for (let attempt = 1; release === null; attempt++) {
@@ -4494,8 +5087,8 @@ var BranchMetadataCorruptError = class extends Error {
4494
5087
  };
4495
5088
  var BranchMetadataFileManager = class _BranchMetadataFileManager {
4496
5089
  constructor(branchRoot, baseRoot, options) {
4497
- this.branchRoot = path14.resolve(branchRoot);
4498
- this.filePath = path14.join(this.branchRoot, BRANCH_META_DIR, BRANCH_META_FILE);
5090
+ this.branchRoot = path15.resolve(branchRoot);
5091
+ this.filePath = path15.join(this.branchRoot, BRANCH_META_DIR, BRANCH_META_FILE);
4499
5092
  this.baseRoot = baseRoot;
4500
5093
  this.settleMs = options?.settleMs;
4501
5094
  }
@@ -4504,10 +5097,10 @@ var BranchMetadataFileManager = class _BranchMetadataFileManager {
4504
5097
  * Use this for read-only access (e.g., in registry scanning or loadBranchContext).
4505
5098
  */
4506
5099
  static async loadOnly(branchRoot) {
4507
- const resolvedRoot = path14.resolve(branchRoot);
4508
- const filePath = path14.join(resolvedRoot, BRANCH_META_DIR, BRANCH_META_FILE);
5100
+ const resolvedRoot = path15.resolve(branchRoot);
5101
+ const filePath = path15.join(resolvedRoot, BRANCH_META_DIR, BRANCH_META_FILE);
4509
5102
  try {
4510
- const raw = await fs13.readFile(filePath, "utf8");
5103
+ const raw = await fs14.readFile(filePath, "utf8");
4511
5104
  return JSON.parse(raw);
4512
5105
  } catch (err) {
4513
5106
  if (isNotFoundError(err)) {
@@ -4528,7 +5121,7 @@ var BranchMetadataFileManager = class _BranchMetadataFileManager {
4528
5121
  }
4529
5122
  async load() {
4530
5123
  try {
4531
- const raw = await fs13.readFile(this.filePath, "utf8");
5124
+ const raw = await fs14.readFile(this.filePath, "utf8");
4532
5125
  const parsed = JSON.parse(raw);
4533
5126
  const version = parsed.version ?? 0;
4534
5127
  return { meta: parsed, version };
@@ -4590,7 +5183,7 @@ var BranchMetadataFileManager = class _BranchMetadataFileManager {
4590
5183
  */
4591
5184
  async save(incoming) {
4592
5185
  try {
4593
- await fs13.stat(this.branchRoot);
5186
+ await fs14.stat(this.branchRoot);
4594
5187
  } catch (err) {
4595
5188
  if (isNotFoundError(err)) {
4596
5189
  throw new BranchMetadataConflictError("Branch no longer exists");
@@ -4678,8 +5271,8 @@ var loadBranchContext = async (options) => {
4678
5271
  init_operating_mode();
4679
5272
 
4680
5273
  // dist/git-manager.js
4681
- import fs14 from "node:fs/promises";
4682
- import path15 from "node:path";
5274
+ import fs15 from "node:fs/promises";
5275
+ import path16 from "node:path";
4683
5276
  import { simpleGit as simpleGit2 } from "simple-git";
4684
5277
 
4685
5278
  // dist/utils/git.js
@@ -4739,11 +5332,11 @@ function gitChildEnv(overrides) {
4739
5332
  var remoteInitLocks = /* @__PURE__ */ new Map();
4740
5333
  var GITHUB_TRACKING_REF_PREFIX = "refs/remotes/github/";
4741
5334
  async function ensureGitExcludePattern(repoPath, pattern) {
4742
- const excludePath = path15.join(repoPath, ".git", "info", "exclude");
4743
- await fs14.mkdir(path15.dirname(excludePath), { recursive: true });
5335
+ const excludePath = path16.join(repoPath, ".git", "info", "exclude");
5336
+ await fs15.mkdir(path16.dirname(excludePath), { recursive: true });
4744
5337
  let content = "";
4745
5338
  try {
4746
- content = await fs14.readFile(excludePath, "utf-8");
5339
+ content = await fs15.readFile(excludePath, "utf-8");
4747
5340
  } catch (err) {
4748
5341
  if (!isNotFoundError(err))
4749
5342
  throw err;
@@ -4755,7 +5348,7 @@ async function ensureGitExcludePattern(repoPath, pattern) {
4755
5348
  }
4756
5349
  const needsLeadingNewline = content.length > 0 && !content.endsWith("\n");
4757
5350
  const newContent = content + (needsLeadingNewline ? "\n" : "") + pattern + "\n";
4758
- await fs14.writeFile(excludePath, newContent, "utf-8");
5351
+ await fs15.writeFile(excludePath, newContent, "utf-8");
4759
5352
  log5.debug("git", "Added pattern to .git/info/exclude", { pattern });
4760
5353
  }
4761
5354
  var GitConflictError = class extends Error {
@@ -4776,12 +5369,12 @@ var GitRemoteRefMissingError = class extends Error {
4776
5369
  };
4777
5370
  var GitManager = class _GitManager {
4778
5371
  constructor(options, gitOptions) {
4779
- this.repoPath = path15.resolve(options.repoPath);
5372
+ this.repoPath = path16.resolve(options.repoPath);
4780
5373
  this.baseBranch = options.baseBranch ?? "main";
4781
5374
  this.remote = options.remote ?? "origin";
4782
5375
  this.skipIndexMarker = options.skipIndexMarker ?? false;
4783
5376
  this.git = simpleGit2({ baseDir: this.repoPath, ...gitOptions });
4784
- this.git.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path15.dirname(this.repoPath) }));
5377
+ this.git.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path16.dirname(this.repoPath) }));
4785
5378
  }
4786
5379
  static async cloneRepo(remoteUrl, targetPath, baseBranch = "main") {
4787
5380
  log5.debug("git", "Cloning repository", {
@@ -4823,11 +5416,11 @@ var GitManager = class _GitManager {
4823
5416
  remotePath: options.remotePath,
4824
5417
  baseBranch: options.baseBranch
4825
5418
  });
4826
- const remoteParent = path15.dirname(options.remotePath);
5419
+ const remoteParent = path16.dirname(options.remotePath);
4827
5420
  releaseLock = await acquireProvisioningLock(remoteParent, ".remote-init.lock");
4828
5421
  let remoteExists = false;
4829
5422
  try {
4830
- const stat = await fs14.stat(options.remotePath);
5423
+ const stat = await fs15.stat(options.remotePath);
4831
5424
  remoteExists = stat.isDirectory();
4832
5425
  } catch (err) {
4833
5426
  if (!isNotFoundError(err))
@@ -5079,7 +5672,7 @@ var GitManager = class _GitManager {
5079
5672
  */
5080
5673
  static async validateGitRepoExists(repoPath) {
5081
5674
  try {
5082
- const stat = await fs14.stat(path15.join(repoPath, ".git"));
5675
+ const stat = await fs15.stat(path16.join(repoPath, ".git"));
5083
5676
  if (!stat.isDirectory()) {
5084
5677
  throw new Error(`Expected git repo at ${repoPath}`);
5085
5678
  }
@@ -5157,7 +5750,7 @@ var GitManager = class _GitManager {
5157
5750
  }
5158
5751
  if (config.autoDetectRemotePath) {
5159
5752
  try {
5160
- const stat = await fs14.stat(config.autoDetectRemotePath);
5753
+ const stat = await fs15.stat(config.autoDetectRemotePath);
5161
5754
  if (stat.isDirectory()) {
5162
5755
  log5.debug("git", "Auto-detected local remote", {
5163
5756
  path: config.autoDetectRemotePath
@@ -5170,8 +5763,8 @@ var GitManager = class _GitManager {
5170
5763
  if (config.shouldAutoInitLocal) {
5171
5764
  const gitRoot = await this.findGitRoot();
5172
5765
  const sourceRoot = options.sourceRoot;
5173
- const sourcePath = sourceRoot ? path15.resolve(gitRoot, sourceRoot) : gitRoot;
5174
- const localRemotePath = path15.join(sourcePath, config.defaultRemotePath);
5766
+ const sourcePath = sourceRoot ? path16.resolve(gitRoot, sourceRoot) : gitRoot;
5767
+ const localRemotePath = path16.join(sourcePath, config.defaultRemotePath);
5175
5768
  await this.ensureLocalSimulatedRemote({
5176
5769
  remotePath: localRemotePath,
5177
5770
  sourcePath: gitRoot,
@@ -5199,7 +5792,7 @@ var GitManager = class _GitManager {
5199
5792
  static async repoExistsAt(workspacePath) {
5200
5793
  try {
5201
5794
  const checkGit = simpleGit2({ baseDir: workspacePath });
5202
- checkGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path15.dirname(workspacePath) }));
5795
+ checkGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path16.dirname(workspacePath) }));
5203
5796
  await checkGit.raw(["rev-parse", "--git-dir"]);
5204
5797
  return true;
5205
5798
  } catch {
@@ -5221,19 +5814,19 @@ var GitManager = class _GitManager {
5221
5814
  const baseBranch = await resolveBaseBranch({
5222
5815
  defaultBaseBranch: options.baseBranch,
5223
5816
  mode: options.mode,
5224
- detectFrom: options.sourceRoot ? path15.resolve(process.cwd(), options.sourceRoot) : process.cwd()
5817
+ detectFrom: options.sourceRoot ? path16.resolve(process.cwd(), options.sourceRoot) : process.cwd()
5225
5818
  });
5226
5819
  const remoteName = options.remoteName ?? "origin";
5227
5820
  const repoExists = await _GitManager.repoExistsAt(options.workspacePath);
5228
5821
  if (!repoExists) {
5229
- const gitPath = path15.join(options.workspacePath, ".git");
5822
+ const gitPath = path16.join(options.workspacePath, ".git");
5230
5823
  try {
5231
- const stat = await fs14.stat(gitPath);
5824
+ const stat = await fs15.stat(gitPath);
5232
5825
  if (stat.isDirectory()) {
5233
5826
  log5.debug("git", "Removing corrupt .git directory", {
5234
5827
  workspacePath: options.workspacePath
5235
5828
  });
5236
- await fs14.rm(gitPath, { recursive: true });
5829
+ await fs15.rm(gitPath, { recursive: true });
5237
5830
  }
5238
5831
  } catch (cleanupErr) {
5239
5832
  if (!isNotFoundError(cleanupErr))
@@ -5260,7 +5853,7 @@ var GitManager = class _GitManager {
5260
5853
  }
5261
5854
  justCloned = true;
5262
5855
  const freshGit = simpleGit2({ baseDir: options.workspacePath });
5263
- freshGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path15.dirname(options.workspacePath) }));
5856
+ freshGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path16.dirname(options.workspacePath) }));
5264
5857
  await freshGit.addConfig("canopycms.managed", "true");
5265
5858
  await freshGit.addConfig("user.name", options.gitBotAuthorName);
5266
5859
  await freshGit.addConfig("user.email", options.gitBotAuthorEmail);
@@ -5611,9 +6204,9 @@ var GitManager = class _GitManager {
5611
6204
  } catch {
5612
6205
  }
5613
6206
  for (const [filePath, content] of Object.entries(initialFiles)) {
5614
- const absolutePath = path15.join(this.repoPath, filePath);
5615
- await fs14.mkdir(path15.dirname(absolutePath), { recursive: true });
5616
- await fs14.writeFile(absolutePath, content, "utf-8");
6207
+ const absolutePath = path16.join(this.repoPath, filePath);
6208
+ await fs15.mkdir(path16.dirname(absolutePath), { recursive: true });
6209
+ await fs15.writeFile(absolutePath, content, "utf-8");
5617
6210
  await this.git.add(filePath);
5618
6211
  }
5619
6212
  await this.git.commit("Initialize settings branch", ["--allow-empty"]);
@@ -5642,7 +6235,7 @@ var BranchWorkspaceManager = class {
5642
6235
  branchName: options.branchName,
5643
6236
  mode: options.mode
5644
6237
  });
5645
- releaseLock = await acquireProvisioningLock(path16.dirname(options.branchRoot), `.${path16.basename(options.branchRoot)}.init.lock`);
6238
+ releaseLock = await acquireProvisioningLock(path17.dirname(options.branchRoot), `.${path17.basename(options.branchRoot)}.init.lock`);
5646
6239
  await GitManager.initializeWorkspace({
5647
6240
  workspacePath: options.branchRoot,
5648
6241
  branchName: options.branchName,
@@ -5683,7 +6276,7 @@ var BranchWorkspaceManager = class {
5683
6276
  const baseBranch = await resolveBaseBranch({
5684
6277
  defaultBaseBranch: this.config.defaultBaseBranch,
5685
6278
  mode,
5686
- detectFrom: this.config.sourceRoot ? path16.resolve(process.cwd(), this.config.sourceRoot) : void 0
6279
+ detectFrom: this.config.sourceRoot ? path17.resolve(process.cwd(), this.config.sourceRoot) : void 0
5687
6280
  });
5688
6281
  await this.ensureGitWorkspace({
5689
6282
  branchRoot,
@@ -5767,9 +6360,9 @@ async function resolveBranchRoot(config) {
5767
6360
  }
5768
6361
 
5769
6362
  // dist/content-listing.js
5770
- import fs15 from "node:fs/promises";
5771
- import path17 from "node:path";
5772
- import matter2 from "gray-matter";
6363
+ import fs16 from "node:fs/promises";
6364
+ import path18 from "node:path";
6365
+ import matter3 from "gray-matter";
5773
6366
  import { parse as yamlParse2 } from "yaml";
5774
6367
  var log7 = createDebugLogger({ prefix: "ContentListing" });
5775
6368
  var normalizePath = (root, target) => {
@@ -5781,15 +6374,15 @@ var normalizePath = (root, target) => {
5781
6374
  };
5782
6375
  var readEntryData = async (filePath, format, bodyFieldName = "body") => {
5783
6376
  try {
5784
- const raw = await fs15.readFile(filePath, "utf8");
6377
+ const raw = await fs16.readFile(filePath, "utf8");
5785
6378
  if (format === "json") {
5786
6379
  return asRecord(JSON.parse(raw));
5787
6380
  }
5788
6381
  if (format === "yaml") {
5789
6382
  return asRecord(yamlParse2(raw));
5790
6383
  }
5791
- const parsed = matter2(raw);
5792
- const data = parsed.data ?? {};
6384
+ const parsed = matter3(raw);
6385
+ const data = { ...parsed.data ?? {} };
5793
6386
  if (parsed.content) {
5794
6387
  data[bodyFieldName] = parsed.content;
5795
6388
  }
@@ -5801,30 +6394,19 @@ var readEntryData = async (filePath, format, bodyFieldName = "body") => {
5801
6394
  return {};
5802
6395
  }
5803
6396
  };
5804
- var parseTypedFilename = (filename, entryTypes) => {
5805
- if (filename.startsWith("."))
5806
- return null;
5807
- const lastDot = filename.lastIndexOf(".");
5808
- if (lastDot === -1)
5809
- return null;
5810
- const nameWithoutExt = filename.slice(0, lastDot);
5811
- const parts = nameWithoutExt.split(".");
5812
- if (parts.length < 3)
5813
- return null;
5814
- const potentialType = parts[0];
5815
- if (entryTypes && !entryTypes.some((e) => e.name === potentialType)) {
5816
- return null;
5817
- }
5818
- const id = parts[parts.length - 1];
5819
- if (!isValidId(id))
5820
- return null;
5821
- const slug = parts.slice(1, -1).join(".").toLowerCase();
6397
+ var createReferenceResolver = (branchRoot, flatSchema, contentRootName) => ({
6398
+ store: new ContentStore(branchRoot, flatSchema, { contentRootName }),
6399
+ cache: createReferenceResolveCache()
6400
+ });
6401
+ var resolveCollectionItemReferences = async (items, collection, resolver) => Promise.all(items.map(async (item) => {
6402
+ const fields = collection.entries?.find((e) => e.name === item.entryType)?.schema;
6403
+ if (!fields)
6404
+ return item;
5822
6405
  return {
5823
- type: potentialType,
5824
- slug,
5825
- id
6406
+ ...item,
6407
+ data: await resolver.store.resolveReferences(item.data, fields, resolver.cache)
5826
6408
  };
5827
- };
6409
+ }));
5828
6410
  async function listEntries(branchRoot, flatSchema, contentRootName, options, visibility) {
5829
6411
  const rootPath = options?.rootPath ?? contentRootName;
5830
6412
  const extract = options?.extract;
@@ -5833,10 +6415,12 @@ async function listEntries(branchRoot, flatSchema, contentRootName, options, vis
5833
6415
  const collections = flatSchema.filter((item) => item.type === "collection" && item.entries !== void 0 && (item.logicalPath === rootPath || item.logicalPath.startsWith(`${rootPath}/`)));
5834
6416
  const shouldInclude = visibility?.shouldInclude;
5835
6417
  const skippedFiles = [];
6418
+ const resolver = options?.resolveReferences ? createReferenceResolver(branchRoot, flatSchema, contentRootName) : null;
5836
6419
  const collectionResults = await Promise.all(collections.map(async (collection) => {
5837
6420
  const entries = await listCollectionEntries(branchRoot, collection, (file) => skippedFiles.push(file));
5838
6421
  const visible = shouldInclude ? entries.filter((e) => shouldInclude(e.physicalPath)) : entries;
5839
- return visible.map((entry) => ({ entry, collection }));
6422
+ const resolved = resolver ? await resolveCollectionItemReferences(visible, collection, resolver) : visible;
6423
+ return resolved.map((entry) => ({ entry, collection }));
5840
6424
  }));
5841
6425
  if (isBuildMode() && skippedFiles.length > 0) {
5842
6426
  const lines = skippedFiles.map(({ filename, collectionPath }) => ` - ${collectionPath}/${filename}`);
@@ -5850,8 +6434,7 @@ Expected {type}.{slug}.{id}.{ext} with a known entry type (a name declared in th
5850
6434
  for (const { entry, collection } of results) {
5851
6435
  const pathWithoutRoot = entry.logicalPath.startsWith(contentPrefix) ? entry.logicalPath.slice(contentPrefix.length) : entry.logicalPath;
5852
6436
  const pathSegments = pathWithoutRoot.split("/").filter(Boolean);
5853
- const urlSegments = entry.slug === "index" ? pathSegments.slice(0, -1) : pathSegments;
5854
- const urlPath = urlSegments.length > 0 ? `/${urlSegments.join("/")}`.toLowerCase() : "/";
6437
+ const urlPath = computeEntryUrl(entry.collectionPath, entry.slug, contentRootName);
5855
6438
  const raw = entry.data;
5856
6439
  const meta = {
5857
6440
  entryPath: entry.logicalPath,
@@ -5913,7 +6496,7 @@ var listCollectionEntries = async (root, collection, onSkip) => {
5913
6496
  normalizePath(root, collectionRoot);
5914
6497
  let dirents;
5915
6498
  try {
5916
- dirents = await fs15.readdir(collectionRoot, { withFileTypes: true });
6499
+ dirents = await fs16.readdir(collectionRoot, { withFileTypes: true });
5917
6500
  } catch (err) {
5918
6501
  if (isNotFoundError(err))
5919
6502
  return [];
@@ -5921,7 +6504,7 @@ var listCollectionEntries = async (root, collection, onSkip) => {
5921
6504
  }
5922
6505
  const files = dirents.filter((d) => d.isFile() && validExts.some((ext) => d.name.endsWith(ext)) && d.name !== ".collection.json").sort((a, b) => a.name.localeCompare(b.name));
5923
6506
  const entries = await Promise.all(files.map(async (file) => {
5924
- const absolutePath = path17.join(collectionRoot, file.name);
6507
+ const absolutePath = path18.join(collectionRoot, file.name);
5925
6508
  const relativePath = normalizePath(root, absolutePath);
5926
6509
  const parsed = parseTypedFilename(file.name, entryTypes);
5927
6510
  if (!parsed) {
@@ -5936,7 +6519,7 @@ var listCollectionEntries = async (root, collection, onSkip) => {
5936
6519
  const format = entryType?.format || "json";
5937
6520
  const bodyField = entryType?.schema ? findBodyFieldName(entryType.schema) : "body";
5938
6521
  const [stats, data] = await Promise.all([
5939
- fs15.stat(absolutePath),
6522
+ fs16.stat(absolutePath),
5940
6523
  readEntryData(absolutePath, format, bodyField)
5941
6524
  ]);
5942
6525
  const item = {
@@ -5974,7 +6557,7 @@ function isValidCropRect(x, y, w, h) {
5974
6557
  }
5975
6558
 
5976
6559
  // dist/validation/field-traversal.js
5977
- function isPlainRecord(value) {
6560
+ function isPlainRecord2(value) {
5978
6561
  return typeof value === "object" && value !== null && !Array.isArray(value);
5979
6562
  }
5980
6563
  function resolveBlockItem(blockField, item) {
@@ -5984,21 +6567,21 @@ function resolveBlockItem(blockField, item) {
5984
6567
  const template = blockField.templates?.find((t) => t.name === templateName);
5985
6568
  if (!template?.fields)
5986
6569
  return void 0;
5987
- const data = isPlainRecord(item.value) ? item.value : item;
6570
+ const data = isPlainRecord2(item.value) ? item.value : item;
5988
6571
  return { fields: template.fields, data };
5989
6572
  }
5990
6573
 
5991
6574
  // dist/validation/entry-validator.js
5992
6575
  var KNOWN_FIELD_TYPES = new Set(fieldTypes);
5993
6576
  var STRING_FIELD_TYPES = /* @__PURE__ */ new Set(["string", "markdown", "mdx", "code"]);
5994
- function isPlainRecord2(value) {
6577
+ function isPlainRecord3(value) {
5995
6578
  return typeof value === "object" && value !== null && !Array.isArray(value);
5996
6579
  }
5997
6580
  function isPositiveInt(value) {
5998
6581
  return typeof value === "number" && Number.isInteger(value) && value > 0;
5999
6582
  }
6000
6583
  function isValidImageCropValue(value) {
6001
- if (!isPlainRecord2(value))
6584
+ if (!isPlainRecord3(value))
6002
6585
  return false;
6003
6586
  const { x, y, w, h } = value;
6004
6587
  if (typeof x !== "number" || typeof y !== "number" || typeof w !== "number" || typeof h !== "number") {
@@ -6006,79 +6589,79 @@ function isValidImageCropValue(value) {
6006
6589
  }
6007
6590
  return isValidCropRect(x, y, w, h);
6008
6591
  }
6009
- function validateImageValue(field, value, path20) {
6010
- if (!isPlainRecord2(value)) {
6011
- return [{ fieldPath: path20, message: "Expected an image object with { src, alt }" }];
6592
+ function validateImageValue(field, value, path21) {
6593
+ if (!isPlainRecord3(value)) {
6594
+ return [{ fieldPath: path21, message: "Expected an image object with { src, alt }" }];
6012
6595
  }
6013
6596
  const errors = [];
6014
6597
  if (typeof value.src !== "string" || value.src.trim() === "") {
6015
- errors.push({ fieldPath: `${path20}.src`, message: "Image src is required" });
6598
+ errors.push({ fieldPath: `${path21}.src`, message: "Image src is required" });
6016
6599
  }
6017
6600
  if (typeof value.alt !== "string") {
6018
- errors.push({ fieldPath: `${path20}.alt`, message: "Image alt text is required" });
6601
+ errors.push({ fieldPath: `${path21}.alt`, message: "Image alt text is required" });
6019
6602
  } else if (value.alt.trim() === "" && field.altOptional !== true) {
6020
- errors.push({ fieldPath: `${path20}.alt`, message: "Image alt text is required" });
6603
+ errors.push({ fieldPath: `${path21}.alt`, message: "Image alt text is required" });
6021
6604
  }
6022
6605
  if (value.width !== void 0 && !isPositiveInt(value.width)) {
6023
- errors.push({ fieldPath: `${path20}.width`, message: "Image width must be a positive integer" });
6606
+ errors.push({ fieldPath: `${path21}.width`, message: "Image width must be a positive integer" });
6024
6607
  }
6025
6608
  if (value.height !== void 0 && !isPositiveInt(value.height)) {
6026
6609
  errors.push({
6027
- fieldPath: `${path20}.height`,
6610
+ fieldPath: `${path21}.height`,
6028
6611
  message: "Image height must be a positive integer"
6029
6612
  });
6030
6613
  }
6031
6614
  if (value.crop !== void 0 && !isValidImageCropValue(value.crop)) {
6032
- errors.push({ fieldPath: `${path20}.crop`, message: "Invalid image crop rect" });
6615
+ errors.push({ fieldPath: `${path21}.crop`, message: "Invalid image crop rect" });
6033
6616
  }
6034
6617
  return errors;
6035
6618
  }
6036
6619
  function referenceValueId(value) {
6037
6620
  if (typeof value === "string")
6038
6621
  return value;
6039
- if (isPlainRecord2(value) && typeof value.id === "string")
6622
+ if (isPlainRecord3(value) && typeof value.id === "string")
6040
6623
  return value.id;
6041
6624
  return void 0;
6042
6625
  }
6043
6626
  var selectOptionValues = (field) => (field.options ?? []).map((opt) => typeof opt === "string" ? opt : opt.value);
6044
- function validateScalar(field, value, path20) {
6627
+ function validateScalar(field, value, path21) {
6045
6628
  switch (field.type) {
6046
6629
  case "number":
6047
6630
  if (typeof value !== "number" || !Number.isFinite(value)) {
6048
- return [{ fieldPath: path20, message: "Expected a number" }];
6631
+ return [{ fieldPath: path21, message: "Expected a number" }];
6049
6632
  }
6050
6633
  return [];
6051
6634
  case "boolean":
6052
6635
  if (typeof value !== "boolean") {
6053
- return [{ fieldPath: path20, message: "Expected true or false" }];
6636
+ return [{ fieldPath: path21, message: "Expected true or false" }];
6054
6637
  }
6055
6638
  return [];
6056
6639
  case "datetime":
6057
6640
  if (typeof value !== "string" || value !== "" && Number.isNaN(Date.parse(value))) {
6058
- return [{ fieldPath: path20, message: "Expected a valid date/time string" }];
6641
+ return [{ fieldPath: path21, message: "Expected a valid date/time string" }];
6059
6642
  }
6060
6643
  return [];
6061
6644
  case "select": {
6062
6645
  if (typeof value !== "string") {
6063
- return [{ fieldPath: path20, message: "Expected a selection" }];
6646
+ return [{ fieldPath: path21, message: "Expected a selection" }];
6064
6647
  }
6065
6648
  const options = selectOptionValues(field);
6066
6649
  if (value !== "" && !options.includes(value)) {
6067
- return [{ fieldPath: path20, message: `Must be one of: ${options.join(", ")}` }];
6650
+ return [{ fieldPath: path21, message: `Must be one of: ${options.join(", ")}` }];
6068
6651
  }
6069
6652
  return [];
6070
6653
  }
6071
6654
  case "reference":
6072
6655
  if (referenceValueId(value) === void 0) {
6073
- return [{ fieldPath: path20, message: "Expected a reference id" }];
6656
+ return [{ fieldPath: path21, message: "Expected a reference id" }];
6074
6657
  }
6075
6658
  return [];
6076
6659
  case "image":
6077
- return validateImageValue(field, value, path20);
6660
+ return validateImageValue(field, value, path21);
6078
6661
  default:
6079
6662
  if (STRING_FIELD_TYPES.has(field.type)) {
6080
6663
  if (typeof value !== "string") {
6081
- return [{ fieldPath: path20, message: "Expected text" }];
6664
+ return [{ fieldPath: path21, message: "Expected text" }];
6082
6665
  }
6083
6666
  }
6084
6667
  return [];
@@ -6101,12 +6684,12 @@ function validateEntryData(fields, data, pathPrefix = "") {
6101
6684
  errors.push(...validateEntryData(field.fields, data, pathPrefix));
6102
6685
  continue;
6103
6686
  }
6104
- const path20 = pathPrefix ? `${pathPrefix}.${field.name}` : field.name;
6687
+ const path21 = pathPrefix ? `${pathPrefix}.${field.name}` : field.name;
6105
6688
  const value = data[field.name];
6106
6689
  const isRequired = "required" in field && field.required === true;
6107
6690
  if (isRequired && isEmptyForRequired(field, value)) {
6108
6691
  const message = Array.isArray(value) ? "At least one item is required" : "This field is required";
6109
- errors.push({ fieldPath: path20, message });
6692
+ errors.push({ fieldPath: path21, message });
6110
6693
  continue;
6111
6694
  }
6112
6695
  if (value === void 0 || value === null)
@@ -6114,12 +6697,12 @@ function validateEntryData(fields, data, pathPrefix = "") {
6114
6697
  if (field.type === "block") {
6115
6698
  const blockField = field;
6116
6699
  if (!Array.isArray(value)) {
6117
- errors.push({ fieldPath: path20, message: "Expected a list of blocks" });
6700
+ errors.push({ fieldPath: path21, message: "Expected a list of blocks" });
6118
6701
  continue;
6119
6702
  }
6120
6703
  value.forEach((item, index) => {
6121
- const itemPath = `${path20}[${index}]`;
6122
- if (!isPlainRecord2(item)) {
6704
+ const itemPath = `${path21}[${index}]`;
6705
+ if (!isPlainRecord3(item)) {
6123
6706
  errors.push({ fieldPath: itemPath, message: "Expected a block object" });
6124
6707
  return;
6125
6708
  }
@@ -6140,12 +6723,12 @@ function validateEntryData(fields, data, pathPrefix = "") {
6140
6723
  const objectField = field;
6141
6724
  if (objectField.list) {
6142
6725
  if (!Array.isArray(value)) {
6143
- errors.push({ fieldPath: path20, message: "Expected a list of items" });
6726
+ errors.push({ fieldPath: path21, message: "Expected a list of items" });
6144
6727
  continue;
6145
6728
  }
6146
6729
  value.forEach((item, index) => {
6147
- const itemPath = `${path20}[${index}]`;
6148
- if (!isPlainRecord2(item)) {
6730
+ const itemPath = `${path21}[${index}]`;
6731
+ if (!isPlainRecord3(item)) {
6149
6732
  errors.push({ fieldPath: itemPath, message: "Expected an object" });
6150
6733
  return;
6151
6734
  }
@@ -6153,28 +6736,28 @@ function validateEntryData(fields, data, pathPrefix = "") {
6153
6736
  errors.push(...validateEntryData(objectField.fields, item, itemPath));
6154
6737
  }
6155
6738
  });
6156
- } else if (!isPlainRecord2(value)) {
6157
- errors.push({ fieldPath: path20, message: "Expected an object" });
6739
+ } else if (!isPlainRecord3(value)) {
6740
+ errors.push({ fieldPath: path21, message: "Expected an object" });
6158
6741
  } else if (objectField.fields) {
6159
- errors.push(...validateEntryData(objectField.fields, value, path20));
6742
+ errors.push(...validateEntryData(objectField.fields, value, path21));
6160
6743
  }
6161
6744
  continue;
6162
6745
  }
6163
6746
  if ("list" in field && field.list) {
6164
6747
  if (!Array.isArray(value)) {
6165
- errors.push({ fieldPath: path20, message: "Expected a list of values" });
6748
+ errors.push({ fieldPath: path21, message: "Expected a list of values" });
6166
6749
  continue;
6167
6750
  }
6168
6751
  value.forEach((item, index) => {
6169
- errors.push(...validateScalar(field, item, `${path20}[${index}]`));
6752
+ errors.push(...validateScalar(field, item, `${path21}[${index}]`));
6170
6753
  });
6171
6754
  continue;
6172
6755
  }
6173
6756
  if (Array.isArray(value) && KNOWN_FIELD_TYPES.has(field.type)) {
6174
- errors.push({ fieldPath: path20, message: "Expected a single value, not a list" });
6757
+ errors.push({ fieldPath: path21, message: "Expected a single value, not a list" });
6175
6758
  continue;
6176
6759
  }
6177
- errors.push(...validateScalar(field, value, path20));
6760
+ errors.push(...validateScalar(field, value, path21));
6178
6761
  }
6179
6762
  return errors;
6180
6763
  }
@@ -6222,6 +6805,38 @@ These are likely abandoned create-scaffolds (empty entries left behind when a cr
6222
6805
  }
6223
6806
 
6224
6807
  // dist/build/generate-ai-content.js
6808
+ var GENERATED_RECORD_FILENAME = ".canopy-generated.json";
6809
+ async function readGeneratedRecord(outputDir) {
6810
+ try {
6811
+ const raw = await fs17.readFile(path19.join(outputDir, GENERATED_RECORD_FILENAME), "utf-8");
6812
+ const parsed = JSON.parse(raw);
6813
+ if (typeof parsed !== "object" || parsed === null || !("files" in parsed))
6814
+ return [];
6815
+ const { files } = parsed;
6816
+ if (!Array.isArray(files))
6817
+ return [];
6818
+ return files.filter((entry) => typeof entry === "string");
6819
+ } catch {
6820
+ return [];
6821
+ }
6822
+ }
6823
+ async function writeGeneratedRecord(outputDir, files) {
6824
+ const record = { files: [...files].sort() };
6825
+ await fs17.mkdir(outputDir, { recursive: true });
6826
+ await fs17.writeFile(path19.join(outputDir, GENERATED_RECORD_FILENAME), `${JSON.stringify(record, null, 2)}
6827
+ `, "utf-8");
6828
+ }
6829
+ async function removeEmptyDirsUpward(startDir, stopAt) {
6830
+ let current = startDir;
6831
+ while (current.startsWith(stopAt) && current !== stopAt) {
6832
+ try {
6833
+ await fs17.rmdir(current);
6834
+ } catch {
6835
+ return;
6836
+ }
6837
+ current = path19.dirname(current);
6838
+ }
6839
+ }
6225
6840
  async function generateAIContentFiles(options) {
6226
6841
  const { config, entrySchemaRegistry, outputDir, aiConfig, _testFlatSchema } = options;
6227
6842
  const contentRootName = config.contentRoot || "content";
@@ -6244,18 +6859,40 @@ async function generateAIContentFiles(options) {
6244
6859
  config: aiConfig,
6245
6860
  entryLinkUrl: config.entryLinkUrl
6246
6861
  });
6247
- const absoluteOutputDir = path18.resolve(outputDir) + path18.sep;
6248
- let fileCount = 0;
6249
- for (const [filePath, content] of result.files) {
6250
- const absolutePath = path18.resolve(path18.join(absoluteOutputDir, filePath));
6862
+ const absoluteOutputDir = path19.resolve(outputDir) + path19.sep;
6863
+ const resolveOutputPath = (filePath) => {
6864
+ const absolutePath = path19.resolve(path19.join(absoluteOutputDir, filePath));
6251
6865
  if (!absolutePath.startsWith(absoluteOutputDir)) {
6252
6866
  throw new Error(`Path traversal detected in AI content output: ${filePath}`);
6253
6867
  }
6254
- await fs16.mkdir(path18.dirname(absolutePath), { recursive: true });
6255
- await fs16.writeFile(absolutePath, content, "utf-8");
6868
+ return absolutePath;
6869
+ };
6870
+ const previousFiles = await readGeneratedRecord(absoluteOutputDir);
6871
+ const currentFiles = [...result.files.keys()];
6872
+ await writeGeneratedRecord(absoluteOutputDir, [.../* @__PURE__ */ new Set([...previousFiles, ...currentFiles])]);
6873
+ let fileCount = 0;
6874
+ for (const [filePath, content] of result.files) {
6875
+ const absolutePath = resolveOutputPath(filePath);
6876
+ await fs17.mkdir(path19.dirname(absolutePath), { recursive: true });
6877
+ await fs17.writeFile(absolutePath, content, "utf-8");
6256
6878
  fileCount++;
6257
6879
  }
6258
- return { fileCount, outputDir: absoluteOutputDir };
6880
+ const currentSet = new Set(currentFiles);
6881
+ const stale = previousFiles.filter((filePath) => !currentSet.has(filePath));
6882
+ let removedCount = 0;
6883
+ for (const filePath of stale) {
6884
+ const absolutePath = resolveOutputPath(filePath);
6885
+ try {
6886
+ await fs17.unlink(absolutePath);
6887
+ removedCount++;
6888
+ } catch (err) {
6889
+ if (!isNodeError(err) || err.code !== "ENOENT")
6890
+ throw err;
6891
+ }
6892
+ await removeEmptyDirsUpward(path19.dirname(absolutePath), absoluteOutputDir);
6893
+ }
6894
+ await writeGeneratedRecord(absoluteOutputDir, currentFiles);
6895
+ return { fileCount, removedCount, outputDir: absoluteOutputDir };
6259
6896
  }
6260
6897
 
6261
6898
  // dist/cli/generate-ai-content.js
@@ -6263,7 +6900,7 @@ var jiti = createJiti(import.meta.url);
6263
6900
  async function generateAIContentCLI(options) {
6264
6901
  const { projectDir, outputDir = "public/ai", configPath, appDir = "app" } = options;
6265
6902
  console.log("\nCanopyCMS generate-ai-content\n");
6266
- const canopyConfigPath = path19.join(projectDir, "canopycms.config.ts");
6903
+ const canopyConfigPath = path20.join(projectDir, "canopycms.config.ts");
6267
6904
  let canopyConfigModule;
6268
6905
  try {
6269
6906
  canopyConfigModule = await jiti.import(canopyConfigPath);
@@ -6274,7 +6911,7 @@ async function generateAIContentCLI(options) {
6274
6911
  }
6275
6912
  const configExport = canopyConfigModule.default ?? canopyConfigModule.config ?? canopyConfigModule;
6276
6913
  const serverConfig = typeof configExport === "object" && configExport !== null && "server" in configExport ? configExport.server : configExport;
6277
- const schemasPath = path19.join(projectDir, appDir, "schemas.ts");
6914
+ const schemasPath = path20.join(projectDir, appDir, "schemas.ts");
6278
6915
  let entrySchemaRegistry = {};
6279
6916
  try {
6280
6917
  const schemasModule = await jiti.import(schemasPath);
@@ -6285,7 +6922,7 @@ async function generateAIContentCLI(options) {
6285
6922
  let aiConfig;
6286
6923
  if (configPath) {
6287
6924
  try {
6288
- const aiConfigModule = await jiti.import(path19.resolve(configPath));
6925
+ const aiConfigModule = await jiti.import(path20.resolve(configPath));
6289
6926
  aiConfig = aiConfigModule.aiContentConfig ?? aiConfigModule.default ?? aiConfigModule.config;
6290
6927
  } catch (err) {
6291
6928
  console.error(`Could not load AI config from ${configPath}`);
@@ -6302,7 +6939,7 @@ async function generateAIContentCLI(options) {
6302
6939
  console.error("Make sure canopycms.config.ts uses defineCanopyConfig().");
6303
6940
  process.exit(1);
6304
6941
  }
6305
- const resolvedOutput = path19.resolve(projectDir, outputDir);
6942
+ const resolvedOutput = path20.resolve(projectDir, outputDir);
6306
6943
  console.log(` Output: ${resolvedOutput}`);
6307
6944
  console.log(` Mode: ${serverConfig.mode ?? "dev"}`);
6308
6945
  const result = await generateAIContentFiles({
@@ -6313,6 +6950,9 @@ async function generateAIContentCLI(options) {
6313
6950
  });
6314
6951
  console.log(`
6315
6952
  Generated ${result.fileCount} files`);
6953
+ if (result.removedCount > 0) {
6954
+ console.log(` Removed ${result.removedCount} stale files from the previous run`);
6955
+ }
6316
6956
  console.log(` Output: ${result.outputDir}
6317
6957
  `);
6318
6958
  }