canopycms 0.0.62 → 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 (1204) 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 +15 -7
  7. package/dist/ai/index.js +9 -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 +60 -0
  15. package/dist/ai/to-plain-text.js +301 -0
  16. package/dist/ai/transform-components.d.ts +1 -2
  17. package/dist/ai/transform-components.js +70 -6
  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 +58 -7
  117. package/dist/authorization/branch.js +55 -10
  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 +7 -8
  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 +48 -0
  161. package/dist/build-canopy.js +50 -0
  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 +1192 -495
  166. package/dist/cli/generate-ai-content.d.ts +0 -1
  167. package/dist/cli/generate-ai-content.js +1062 -370
  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/canopycms.config.ts.template +18 -1
  179. package/dist/cli/template-files/deploy-cms.yml.template +8 -6
  180. package/dist/cli/templates.d.ts +0 -1
  181. package/dist/cli/templates.js +0 -1
  182. package/dist/client.d.ts +13 -14
  183. package/dist/client.js +0 -1
  184. package/dist/comment-store.d.ts +0 -1
  185. package/dist/comment-store.js +0 -1
  186. package/dist/config/flatten.d.ts +1 -2
  187. package/dist/config/flatten.js +0 -1
  188. package/dist/config/helpers.d.ts +3 -4
  189. package/dist/config/helpers.js +2 -2
  190. package/dist/config/index.d.ts +10 -11
  191. package/dist/config/index.js +0 -1
  192. package/dist/config/schemas/collection.d.ts +0 -1
  193. package/dist/config/schemas/collection.js +0 -1
  194. package/dist/config/schemas/config.d.ts +7 -4
  195. package/dist/config/schemas/config.js +12 -1
  196. package/dist/config/schemas/field.d.ts +3 -1
  197. package/dist/config/schemas/field.js +5 -1
  198. package/dist/config/schemas/media.d.ts +0 -1
  199. package/dist/config/schemas/media.js +0 -1
  200. package/dist/config/schemas/permissions.d.ts +0 -1
  201. package/dist/config/schemas/permissions.js +0 -1
  202. package/dist/config/types.d.ts +55 -9
  203. package/dist/config/types.js +0 -1
  204. package/dist/config/validation.d.ts +1 -2
  205. package/dist/config/validation.js +0 -1
  206. package/dist/config.d.ts +2 -3
  207. package/dist/config.js +0 -1
  208. package/dist/content-id-index.d.ts +1 -2
  209. package/dist/content-id-index.js +0 -1
  210. package/dist/content-index-generation.d.ts +0 -1
  211. package/dist/content-index-generation.js +0 -1
  212. package/dist/content-index-registry.d.ts +0 -1
  213. package/dist/content-index-registry.js +0 -1
  214. package/dist/content-listing.d.ts +137 -16
  215. package/dist/content-listing.js +175 -41
  216. package/dist/content-reader.d.ts +44 -8
  217. package/dist/content-reader.js +39 -7
  218. package/dist/content-store.d.ts +147 -7
  219. package/dist/content-store.js +294 -22
  220. package/dist/content-tree.d.ts +44 -5
  221. package/dist/content-tree.js +47 -11
  222. package/dist/context.d.ts +38 -15
  223. package/dist/context.js +55 -5
  224. package/dist/dev-content-watcher.d.ts +2 -3
  225. package/dist/dev-content-watcher.js +0 -1
  226. package/dist/editor/BranchManager.d.ts +4 -5
  227. package/dist/editor/BranchManager.js +0 -1
  228. package/dist/editor/CanopyEditor.d.ts +2 -3
  229. package/dist/editor/CanopyEditor.js +1 -2
  230. package/dist/editor/CanopyEditorPage.d.ts +2 -3
  231. package/dist/editor/CanopyEditorPage.js +0 -1
  232. package/dist/editor/CommentsPanel.d.ts +2 -3
  233. package/dist/editor/CommentsPanel.js +0 -1
  234. package/dist/editor/Editor.d.ts +13 -8
  235. package/dist/editor/Editor.js +4 -5
  236. package/dist/editor/EditorPanes.d.ts +0 -1
  237. package/dist/editor/EditorPanes.js +0 -1
  238. package/dist/editor/EntryNavigator.d.ts +1 -2
  239. package/dist/editor/EntryNavigator.js +0 -1
  240. package/dist/editor/FormRenderer.d.ts +2 -3
  241. package/dist/editor/FormRenderer.js +22 -3
  242. package/dist/editor/GroupManager.d.ts +2 -3
  243. package/dist/editor/GroupManager.js +0 -1
  244. package/dist/editor/PermissionManager.d.ts +2 -3
  245. package/dist/editor/PermissionManager.js +0 -1
  246. package/dist/editor/admin/SystemHealthPanel.d.ts +0 -1
  247. package/dist/editor/admin/SystemHealthPanel.js +0 -1
  248. package/dist/editor/admin/useSystemHealth.d.ts +2 -3
  249. package/dist/editor/admin/useSystemHealth.js +0 -1
  250. package/dist/editor/canopy-path.d.ts +0 -1
  251. package/dist/editor/canopy-path.js +0 -1
  252. package/dist/editor/client-reference-resolver.d.ts +13 -4
  253. package/dist/editor/client-reference-resolver.js +18 -8
  254. package/dist/editor/comments/BranchComments.d.ts +2 -3
  255. package/dist/editor/comments/BranchComments.js +0 -1
  256. package/dist/editor/comments/EntryComments.d.ts +1 -2
  257. package/dist/editor/comments/EntryComments.js +0 -1
  258. package/dist/editor/comments/FieldWrapper.d.ts +1 -2
  259. package/dist/editor/comments/FieldWrapper.js +0 -1
  260. package/dist/editor/comments/InlineCommentThread.d.ts +2 -3
  261. package/dist/editor/comments/InlineCommentThread.js +0 -1
  262. package/dist/editor/comments/ThreadCarousel.d.ts +2 -3
  263. package/dist/editor/comments/ThreadCarousel.js +0 -1
  264. package/dist/editor/components/ConfirmDeleteModal.d.ts +0 -1
  265. package/dist/editor/components/ConfirmDeleteModal.js +0 -1
  266. package/dist/editor/components/EditorFooter.d.ts +0 -1
  267. package/dist/editor/components/EditorFooter.js +0 -1
  268. package/dist/editor/components/EditorHeader.d.ts +4 -5
  269. package/dist/editor/components/EditorHeader.js +0 -1
  270. package/dist/editor/components/EditorSidebar.d.ts +1 -2
  271. package/dist/editor/components/EditorSidebar.js +0 -1
  272. package/dist/editor/components/EntryCreateModal.d.ts +0 -1
  273. package/dist/editor/components/EntryCreateModal.js +21 -10
  274. package/dist/editor/components/RenameEntryModal.d.ts +0 -1
  275. package/dist/editor/components/RenameEntryModal.js +0 -1
  276. package/dist/editor/components/UserBadge.d.ts +2 -3
  277. package/dist/editor/components/UserBadge.js +0 -1
  278. package/dist/editor/components/index.d.ts +3 -4
  279. package/dist/editor/components/index.js +0 -1
  280. package/dist/editor/context/ApiClientContext.d.ts +9 -3
  281. package/dist/editor/context/ApiClientContext.js +8 -4
  282. package/dist/editor/context/AssetContext.d.ts +26 -2
  283. package/dist/editor/context/AssetContext.js +23 -3
  284. package/dist/editor/context/EditorStateContext.d.ts +1 -2
  285. package/dist/editor/context/EditorStateContext.js +0 -1
  286. package/dist/editor/context/SWRProvider.d.ts +0 -1
  287. package/dist/editor/context/SWRProvider.js +0 -1
  288. package/dist/editor/context/index.d.ts +4 -5
  289. package/dist/editor/context/index.js +0 -1
  290. package/dist/editor/editor-config.d.ts +2 -3
  291. package/dist/editor/editor-config.js +0 -1
  292. package/dist/editor/editor-utils.d.ts +22 -10
  293. package/dist/editor/editor-utils.js +41 -10
  294. package/dist/editor/fields/BlockField.d.ts +1 -2
  295. package/dist/editor/fields/BlockField.js +0 -1
  296. package/dist/editor/fields/CodeField.d.ts +0 -1
  297. package/dist/editor/fields/CodeField.js +0 -1
  298. package/dist/editor/fields/DateTimeField.d.ts +0 -1
  299. package/dist/editor/fields/DateTimeField.js +0 -1
  300. package/dist/editor/fields/ImageField.d.ts +1 -2
  301. package/dist/editor/fields/ImageField.js +0 -1
  302. package/dist/editor/fields/InlineGroupField.d.ts +2 -3
  303. package/dist/editor/fields/InlineGroupField.js +0 -1
  304. package/dist/editor/fields/MarkdownField.d.ts +0 -1
  305. package/dist/editor/fields/MarkdownField.js +0 -1
  306. package/dist/editor/fields/MdxImageDialog.d.ts +0 -1
  307. package/dist/editor/fields/MdxImageDialog.js +0 -1
  308. package/dist/editor/fields/NumberField.d.ts +0 -1
  309. package/dist/editor/fields/NumberField.js +0 -1
  310. package/dist/editor/fields/NumberListField.d.ts +0 -1
  311. package/dist/editor/fields/NumberListField.js +0 -1
  312. package/dist/editor/fields/ObjectField.d.ts +11 -2
  313. package/dist/editor/fields/ObjectField.js +3 -4
  314. package/dist/editor/fields/ReferenceField.d.ts +0 -1
  315. package/dist/editor/fields/ReferenceField.js +8 -3
  316. package/dist/editor/fields/SelectField.d.ts +0 -1
  317. package/dist/editor/fields/SelectField.js +0 -1
  318. package/dist/editor/fields/StringListField.d.ts +0 -1
  319. package/dist/editor/fields/StringListField.js +0 -1
  320. package/dist/editor/fields/TextField.d.ts +0 -1
  321. package/dist/editor/fields/TextField.js +0 -1
  322. package/dist/editor/fields/ToggleField.d.ts +0 -1
  323. package/dist/editor/fields/ToggleField.js +0 -1
  324. package/dist/editor/fields/entry-link/EntryLinkContext.d.ts +1 -2
  325. package/dist/editor/fields/entry-link/EntryLinkContext.js +0 -1
  326. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +0 -1
  327. package/dist/editor/fields/entry-link/InsertEntryLink.js +0 -1
  328. package/dist/editor/fields/entry-link/index.d.ts +2 -3
  329. package/dist/editor/fields/entry-link/index.js +0 -1
  330. package/dist/editor/group-manager/ExternalGroupsTab.d.ts +1 -2
  331. package/dist/editor/group-manager/ExternalGroupsTab.js +0 -1
  332. package/dist/editor/group-manager/GroupCard.d.ts +1 -2
  333. package/dist/editor/group-manager/GroupCard.js +0 -1
  334. package/dist/editor/group-manager/GroupForm.d.ts +1 -2
  335. package/dist/editor/group-manager/GroupForm.js +0 -1
  336. package/dist/editor/group-manager/InternalGroupsTab.d.ts +1 -2
  337. package/dist/editor/group-manager/InternalGroupsTab.js +0 -1
  338. package/dist/editor/group-manager/MemberList.d.ts +1 -2
  339. package/dist/editor/group-manager/MemberList.js +0 -1
  340. package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts +1 -2
  341. package/dist/editor/group-manager/hooks/useExternalGroupSearch.js +0 -1
  342. package/dist/editor/group-manager/hooks/useGroupState.d.ts +1 -2
  343. package/dist/editor/group-manager/hooks/useGroupState.js +0 -1
  344. package/dist/editor/group-manager/hooks/useUserSearch.d.ts +1 -2
  345. package/dist/editor/group-manager/hooks/useUserSearch.js +0 -1
  346. package/dist/editor/group-manager/index.d.ts +5 -6
  347. package/dist/editor/group-manager/index.js +0 -1
  348. package/dist/editor/group-manager/types.d.ts +4 -5
  349. package/dist/editor/group-manager/types.js +0 -1
  350. package/dist/editor/hooks/index.d.ts +14 -15
  351. package/dist/editor/hooks/index.js +0 -1
  352. package/dist/editor/hooks/useBranchActions.d.ts +0 -1
  353. package/dist/editor/hooks/useBranchActions.js +0 -1
  354. package/dist/editor/hooks/useBranchManager.d.ts +4 -5
  355. package/dist/editor/hooks/useBranchManager.js +0 -1
  356. package/dist/editor/hooks/useBranchesData.d.ts +2 -3
  357. package/dist/editor/hooks/useBranchesData.js +0 -1
  358. package/dist/editor/hooks/useCommentSystem.d.ts +2 -3
  359. package/dist/editor/hooks/useCommentSystem.js +0 -1
  360. package/dist/editor/hooks/useCommentsData.d.ts +2 -3
  361. package/dist/editor/hooks/useCommentsData.js +0 -1
  362. package/dist/editor/hooks/useDraftManager.d.ts +3 -4
  363. package/dist/editor/hooks/useDraftManager.js +0 -1
  364. package/dist/editor/hooks/useEditorLayout.d.ts +1 -2
  365. package/dist/editor/hooks/useEditorLayout.js +0 -1
  366. package/dist/editor/hooks/useEntriesData.d.ts +3 -5
  367. package/dist/editor/hooks/useEntriesData.js +0 -3
  368. package/dist/editor/hooks/useEntryLinkResolution.d.ts +2 -3
  369. package/dist/editor/hooks/useEntryLinkResolution.js +0 -1
  370. package/dist/editor/hooks/useEntryManager.d.ts +5 -7
  371. package/dist/editor/hooks/useEntryManager.js +8 -3
  372. package/dist/editor/hooks/useGroupManager.d.ts +2 -3
  373. package/dist/editor/hooks/useGroupManager.js +0 -1
  374. package/dist/editor/hooks/usePermissionManager.d.ts +2 -3
  375. package/dist/editor/hooks/usePermissionManager.js +0 -1
  376. package/dist/editor/hooks/useReferenceResolution.d.ts +1 -2
  377. package/dist/editor/hooks/useReferenceResolution.js +8 -3
  378. package/dist/editor/hooks/useSchemaManager.d.ts +2 -3
  379. package/dist/editor/hooks/useSchemaManager.js +0 -1
  380. package/dist/editor/hooks/useUserContext.d.ts +1 -2
  381. package/dist/editor/hooks/useUserContext.js +8 -3
  382. package/dist/editor/hooks/useUserMetadata.d.ts +2 -3
  383. package/dist/editor/hooks/useUserMetadata.js +0 -1
  384. package/dist/editor/media/AssetCard.d.ts +1 -2
  385. package/dist/editor/media/AssetCard.js +0 -1
  386. package/dist/editor/media/CropStep.d.ts +1 -2
  387. package/dist/editor/media/CropStep.js +0 -1
  388. package/dist/editor/media/MediaLibrary.d.ts +1 -2
  389. package/dist/editor/media/MediaLibrary.js +0 -1
  390. package/dist/editor/media/MediaLibraryBody.d.ts +1 -2
  391. package/dist/editor/media/MediaLibraryBody.js +0 -1
  392. package/dist/editor/media/crop-math.d.ts +1 -2
  393. package/dist/editor/media/crop-math.js +0 -1
  394. package/dist/editor/media/upload-asset.d.ts +2 -3
  395. package/dist/editor/media/upload-asset.js +0 -1
  396. package/dist/editor/media/upload-constants.d.ts +0 -1
  397. package/dist/editor/media/upload-constants.js +0 -1
  398. package/dist/editor/media/useAssetUpload.d.ts +1 -2
  399. package/dist/editor/media/useAssetUpload.js +0 -1
  400. package/dist/editor/media/xhr-upload.d.ts +0 -1
  401. package/dist/editor/media/xhr-upload.js +0 -1
  402. package/dist/editor/permission-manager/GroupSelector.d.ts +1 -2
  403. package/dist/editor/permission-manager/GroupSelector.js +0 -1
  404. package/dist/editor/permission-manager/PermissionEditor.d.ts +1 -2
  405. package/dist/editor/permission-manager/PermissionEditor.js +0 -1
  406. package/dist/editor/permission-manager/PermissionLevelBadge.d.ts +1 -2
  407. package/dist/editor/permission-manager/PermissionLevelBadge.js +0 -1
  408. package/dist/editor/permission-manager/PermissionTree.d.ts +1 -2
  409. package/dist/editor/permission-manager/PermissionTree.js +0 -1
  410. package/dist/editor/permission-manager/UserSelector.d.ts +1 -2
  411. package/dist/editor/permission-manager/UserSelector.js +0 -1
  412. package/dist/editor/permission-manager/constants.d.ts +1 -2
  413. package/dist/editor/permission-manager/constants.js +0 -1
  414. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +1 -2
  415. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +0 -1
  416. package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts +2 -3
  417. package/dist/editor/permission-manager/hooks/usePermissionTree.js +0 -1
  418. package/dist/editor/permission-manager/index.d.ts +4 -5
  419. package/dist/editor/permission-manager/index.js +0 -1
  420. package/dist/editor/permission-manager/types.d.ts +3 -4
  421. package/dist/editor/permission-manager/types.js +0 -1
  422. package/dist/editor/permission-manager/utils.d.ts +2 -3
  423. package/dist/editor/permission-manager/utils.js +0 -1
  424. package/dist/editor/preview-bridge.d.ts +1 -2
  425. package/dist/editor/preview-bridge.js +0 -1
  426. package/dist/editor/relative-time.d.ts +0 -1
  427. package/dist/editor/relative-time.js +0 -1
  428. package/dist/editor/schema-editor/CollectionEditor.d.ts +4 -5
  429. package/dist/editor/schema-editor/CollectionEditor.js +0 -1
  430. package/dist/editor/schema-editor/EntryTypeEditor.d.ts +2 -3
  431. package/dist/editor/schema-editor/EntryTypeEditor.js +0 -1
  432. package/dist/editor/schema-editor/index.d.ts +2 -3
  433. package/dist/editor/schema-editor/index.js +0 -1
  434. package/dist/editor/theme.d.ts +0 -1
  435. package/dist/editor/theme.js +0 -1
  436. package/dist/editor/utils/env.d.ts +0 -1
  437. package/dist/editor/utils/env.js +0 -1
  438. package/dist/entry-link-resolver.d.ts +1 -2
  439. package/dist/entry-link-resolver.js +0 -1
  440. package/dist/entry-schema-registry.d.ts +3 -4
  441. package/dist/entry-schema-registry.js +6 -2
  442. package/dist/entry-schema.d.ts +342 -11
  443. package/dist/entry-schema.js +170 -1
  444. package/dist/git-manager.d.ts +1 -2
  445. package/dist/git-manager.js +0 -1
  446. package/dist/github-service.d.ts +1 -2
  447. package/dist/github-service.js +0 -1
  448. package/dist/http/handler.d.ts +6 -7
  449. package/dist/http/handler.js +0 -1
  450. package/dist/http/index.d.ts +6 -7
  451. package/dist/http/index.js +0 -1
  452. package/dist/http/router.d.ts +2 -3
  453. package/dist/http/router.js +0 -1
  454. package/dist/http/types.d.ts +0 -1
  455. package/dist/http/types.js +0 -1
  456. package/dist/id.d.ts +2 -3
  457. package/dist/id.js +0 -1
  458. package/dist/index.d.ts +17 -17
  459. package/dist/index.js +7 -1
  460. package/dist/operating-mode/client-safe-strategy.d.ts +1 -2
  461. package/dist/operating-mode/client-safe-strategy.js +0 -1
  462. package/dist/operating-mode/client-unsafe-strategy.d.ts +1 -2
  463. package/dist/operating-mode/client-unsafe-strategy.js +0 -1
  464. package/dist/operating-mode/client.d.ts +3 -4
  465. package/dist/operating-mode/client.js +0 -1
  466. package/dist/operating-mode/deployment-name-fixtures.d.ts +0 -1
  467. package/dist/operating-mode/deployment-name-fixtures.js +0 -1
  468. package/dist/operating-mode/deployment-name.d.ts +0 -1
  469. package/dist/operating-mode/deployment-name.js +0 -1
  470. package/dist/operating-mode/index.d.ts +5 -6
  471. package/dist/operating-mode/index.js +0 -1
  472. package/dist/operating-mode/mode-env.d.ts +1 -2
  473. package/dist/operating-mode/mode-env.js +0 -1
  474. package/dist/operating-mode/types.d.ts +2 -3
  475. package/dist/operating-mode/types.js +0 -1
  476. package/dist/paths/branch-name.d.ts +1 -2
  477. package/dist/paths/branch-name.js +0 -1
  478. package/dist/paths/branch.d.ts +3 -4
  479. package/dist/paths/branch.js +0 -1
  480. package/dist/paths/index.d.ts +8 -9
  481. package/dist/paths/index.js +0 -1
  482. package/dist/paths/normalize-server.d.ts +1 -2
  483. package/dist/paths/normalize-server.js +0 -1
  484. package/dist/paths/normalize.d.ts +1 -2
  485. package/dist/paths/normalize.js +0 -1
  486. package/dist/paths/resolve.d.ts +2 -3
  487. package/dist/paths/resolve.js +0 -1
  488. package/dist/paths/types.d.ts +0 -1
  489. package/dist/paths/types.js +0 -1
  490. package/dist/paths/validation.d.ts +1 -2
  491. package/dist/paths/validation.js +0 -1
  492. package/dist/reference-resolver.d.ts +3 -4
  493. package/dist/reference-resolver.js +0 -1
  494. package/dist/resolve-canopy-user.d.ts +3 -4
  495. package/dist/resolve-canopy-user.js +0 -1
  496. package/dist/resource-generation.d.ts +0 -1
  497. package/dist/resource-generation.js +0 -1
  498. package/dist/schema/index.d.ts +3 -4
  499. package/dist/schema/index.js +0 -1
  500. package/dist/schema/meta-loader.d.ts +3 -4
  501. package/dist/schema/meta-loader.js +3 -3
  502. package/dist/schema/resolver.d.ts +2 -3
  503. package/dist/schema/resolver.js +0 -1
  504. package/dist/schema/schema-store-types.d.ts +2 -3
  505. package/dist/schema/schema-store-types.js +0 -1
  506. package/dist/schema/schema-store.d.ts +6 -7
  507. package/dist/schema/schema-store.js +33 -1
  508. package/dist/schema/types.d.ts +1 -2
  509. package/dist/schema/types.js +0 -1
  510. package/dist/server.d.ts +97 -37
  511. package/dist/server.js +60 -3
  512. package/dist/services.d.ts +10 -12
  513. package/dist/services.js +2 -7
  514. package/dist/settings-workspace.d.ts +12 -15
  515. package/dist/settings-workspace.js +10 -13
  516. package/dist/static/index.d.ts +227 -9
  517. package/dist/static/index.js +302 -14
  518. package/dist/static/seo.d.ts +128 -0
  519. package/dist/static/seo.js +162 -0
  520. package/dist/sync-core.d.ts +0 -1
  521. package/dist/sync-core.js +0 -1
  522. package/dist/task-queue/index.d.ts +2 -3
  523. package/dist/task-queue/index.js +0 -1
  524. package/dist/task-queue/task-queue.d.ts +1 -2
  525. package/dist/task-queue/task-queue.js +0 -1
  526. package/dist/task-queue/types.d.ts +0 -1
  527. package/dist/task-queue/types.js +0 -1
  528. package/dist/types.d.ts +3 -4
  529. package/dist/types.js +0 -1
  530. package/dist/url-collision.d.ts +86 -0
  531. package/dist/url-collision.js +186 -0
  532. package/dist/url-path-resolver.d.ts +11 -2
  533. package/dist/url-path-resolver.js +37 -5
  534. package/dist/user.d.ts +3 -4
  535. package/dist/user.js +0 -1
  536. package/dist/utils/async-mutex.d.ts +0 -1
  537. package/dist/utils/async-mutex.js +0 -1
  538. package/dist/utils/atomic-write.d.ts +0 -1
  539. package/dist/utils/atomic-write.js +0 -1
  540. package/dist/utils/body-field.d.ts +12 -2
  541. package/dist/utils/body-field.js +17 -1
  542. package/dist/utils/content-serialize.d.ts +37 -0
  543. package/dist/utils/content-serialize.js +429 -0
  544. package/dist/utils/content-write-lock.d.ts +12 -16
  545. package/dist/utils/content-write-lock.js +38 -22
  546. package/dist/utils/debug.d.ts +0 -1
  547. package/dist/utils/debug.js +0 -1
  548. package/dist/utils/entry-url.d.ts +17 -1
  549. package/dist/utils/entry-url.js +21 -3
  550. package/dist/utils/error.d.ts +0 -1
  551. package/dist/utils/error.js +0 -1
  552. package/dist/utils/flatten-group-fields.d.ts +1 -2
  553. package/dist/utils/flatten-group-fields.js +0 -1
  554. package/dist/utils/format.d.ts +1 -2
  555. package/dist/utils/format.js +0 -1
  556. package/dist/utils/fs.d.ts +7 -1
  557. package/dist/utils/fs.js +16 -1
  558. package/dist/utils/git.d.ts +1 -2
  559. package/dist/utils/git.js +0 -1
  560. package/dist/utils/logger.d.ts +0 -1
  561. package/dist/utils/logger.js +0 -1
  562. package/dist/utils/occ-json-write.d.ts +0 -1
  563. package/dist/utils/occ-json-write.js +0 -1
  564. package/dist/utils/provisioning-lock.d.ts +17 -4
  565. package/dist/utils/provisioning-lock.js +94 -14
  566. package/dist/utils/sanitize-href.d.ts +46 -1
  567. package/dist/utils/sanitize-href.js +63 -3
  568. package/dist/utils/title-field.d.ts +1 -2
  569. package/dist/utils/title-field.js +0 -1
  570. package/dist/utils/typed-filename.d.ts +61 -0
  571. package/dist/utils/typed-filename.js +92 -0
  572. package/dist/utils/url-prefix.d.ts +121 -0
  573. package/dist/utils/url-prefix.js +158 -0
  574. package/dist/validation/block-structural-keys.d.ts +42 -0
  575. package/dist/validation/block-structural-keys.js +44 -0
  576. package/dist/validation/deletion-checker.d.ts +4 -5
  577. package/dist/validation/deletion-checker.js +0 -1
  578. package/dist/validation/entry-link-validator.d.ts +2 -3
  579. package/dist/validation/entry-link-validator.js +0 -1
  580. package/dist/validation/entry-type-reference-validator.d.ts +1 -2
  581. package/dist/validation/entry-type-reference-validator.js +0 -1
  582. package/dist/validation/entry-validator.d.ts +23 -2
  583. package/dist/validation/entry-validator.js +34 -2
  584. package/dist/validation/field-traversal.d.ts +28 -4
  585. package/dist/validation/field-traversal.js +24 -7
  586. package/dist/validation/reference-validator.d.ts +2 -3
  587. package/dist/validation/reference-validator.js +0 -1
  588. package/dist/worker/cms-worker.d.ts +9 -2
  589. package/dist/worker/cms-worker.js +59 -2
  590. package/dist/worker/log.d.ts +0 -1
  591. package/dist/worker/log.js +0 -1
  592. package/dist/worker/task-queue-config.d.ts +1 -2
  593. package/dist/worker/task-queue-config.js +0 -1
  594. package/dist/worker/task-queue.d.ts +3 -4
  595. package/dist/worker/task-queue.js +0 -1
  596. package/dist/worker/worker-status.d.ts +1 -2
  597. package/dist/worker/worker-status.js +0 -1
  598. package/package.json +2 -6
  599. package/dist/ai/generate.d.ts.map +0 -1
  600. package/dist/ai/generate.js.map +0 -1
  601. package/dist/ai/handler.d.ts.map +0 -1
  602. package/dist/ai/handler.js.map +0 -1
  603. package/dist/ai/index.d.ts.map +0 -1
  604. package/dist/ai/index.js.map +0 -1
  605. package/dist/ai/json-to-markdown.d.ts.map +0 -1
  606. package/dist/ai/json-to-markdown.js.map +0 -1
  607. package/dist/ai/resolve-branch.d.ts.map +0 -1
  608. package/dist/ai/resolve-branch.js.map +0 -1
  609. package/dist/ai/strip-mdx.d.ts.map +0 -1
  610. package/dist/ai/strip-mdx.js.map +0 -1
  611. package/dist/ai/transform-components.d.ts.map +0 -1
  612. package/dist/ai/transform-components.js.map +0 -1
  613. package/dist/ai/types.d.ts.map +0 -1
  614. package/dist/ai/types.js.map +0 -1
  615. package/dist/api/__test__/mock-client.d.ts +0 -231
  616. package/dist/api/__test__/mock-client.d.ts.map +0 -1
  617. package/dist/api/__test__/mock-client.js +0 -388
  618. package/dist/api/__test__/mock-client.js.map +0 -1
  619. package/dist/api/admin-branch-health.d.ts.map +0 -1
  620. package/dist/api/admin-branch-health.js.map +0 -1
  621. package/dist/api/admin.d.ts.map +0 -1
  622. package/dist/api/admin.js.map +0 -1
  623. package/dist/api/assets.d.ts.map +0 -1
  624. package/dist/api/assets.js.map +0 -1
  625. package/dist/api/branch-merge.d.ts.map +0 -1
  626. package/dist/api/branch-merge.js.map +0 -1
  627. package/dist/api/branch-review.d.ts.map +0 -1
  628. package/dist/api/branch-review.js.map +0 -1
  629. package/dist/api/branch-status.d.ts.map +0 -1
  630. package/dist/api/branch-status.js.map +0 -1
  631. package/dist/api/branch-withdraw.d.ts.map +0 -1
  632. package/dist/api/branch-withdraw.js.map +0 -1
  633. package/dist/api/branch.d.ts.map +0 -1
  634. package/dist/api/branch.js.map +0 -1
  635. package/dist/api/client.d.ts.map +0 -1
  636. package/dist/api/client.js.map +0 -1
  637. package/dist/api/comments.d.ts.map +0 -1
  638. package/dist/api/comments.js.map +0 -1
  639. package/dist/api/content.d.ts.map +0 -1
  640. package/dist/api/content.js.map +0 -1
  641. package/dist/api/entries-constants.d.ts.map +0 -1
  642. package/dist/api/entries-constants.js.map +0 -1
  643. package/dist/api/entries.d.ts.map +0 -1
  644. package/dist/api/entries.js.map +0 -1
  645. package/dist/api/github-sync.d.ts.map +0 -1
  646. package/dist/api/github-sync.js.map +0 -1
  647. package/dist/api/groups.d.ts.map +0 -1
  648. package/dist/api/groups.js.map +0 -1
  649. package/dist/api/guards.d.ts.map +0 -1
  650. package/dist/api/guards.js.map +0 -1
  651. package/dist/api/index.d.ts.map +0 -1
  652. package/dist/api/index.js.map +0 -1
  653. package/dist/api/permissions.d.ts.map +0 -1
  654. package/dist/api/permissions.js.map +0 -1
  655. package/dist/api/reference-options.d.ts.map +0 -1
  656. package/dist/api/reference-options.js.map +0 -1
  657. package/dist/api/request-body-hash.d.ts.map +0 -1
  658. package/dist/api/request-body-hash.js.map +0 -1
  659. package/dist/api/resolve-references.d.ts.map +0 -1
  660. package/dist/api/resolve-references.js.map +0 -1
  661. package/dist/api/route-builder.d.ts.map +0 -1
  662. package/dist/api/route-builder.js.map +0 -1
  663. package/dist/api/schema.d.ts.map +0 -1
  664. package/dist/api/schema.js.map +0 -1
  665. package/dist/api/settings-helpers.d.ts.map +0 -1
  666. package/dist/api/settings-helpers.js.map +0 -1
  667. package/dist/api/types.d.ts.map +0 -1
  668. package/dist/api/types.js.map +0 -1
  669. package/dist/api/user.d.ts.map +0 -1
  670. package/dist/api/user.js.map +0 -1
  671. package/dist/api/validators.d.ts.map +0 -1
  672. package/dist/api/validators.js.map +0 -1
  673. package/dist/assets/asset-prefixes.d.ts.map +0 -1
  674. package/dist/assets/asset-prefixes.js.map +0 -1
  675. package/dist/assets/asset-src.d.ts.map +0 -1
  676. package/dist/assets/asset-src.js.map +0 -1
  677. package/dist/assets/asset-url.d.ts.map +0 -1
  678. package/dist/assets/asset-url.js.map +0 -1
  679. package/dist/assets/factory.d.ts.map +0 -1
  680. package/dist/assets/factory.js.map +0 -1
  681. package/dist/assets/finalize.d.ts.map +0 -1
  682. package/dist/assets/finalize.js.map +0 -1
  683. package/dist/assets/index.d.ts.map +0 -1
  684. package/dist/assets/index.js.map +0 -1
  685. package/dist/assets/keys.d.ts.map +0 -1
  686. package/dist/assets/keys.js.map +0 -1
  687. package/dist/assets/pipeline.d.ts.map +0 -1
  688. package/dist/assets/pipeline.js.map +0 -1
  689. package/dist/assets/store-local.d.ts.map +0 -1
  690. package/dist/assets/store-local.js.map +0 -1
  691. package/dist/assets/store-s3.d.ts.map +0 -1
  692. package/dist/assets/store-s3.js.map +0 -1
  693. package/dist/assets/svg-sanitizer.d.ts.map +0 -1
  694. package/dist/assets/svg-sanitizer.js.map +0 -1
  695. package/dist/assets/transform-directives.d.ts.map +0 -1
  696. package/dist/assets/transform-directives.js.map +0 -1
  697. package/dist/assets/transform.d.ts.map +0 -1
  698. package/dist/assets/transform.js.map +0 -1
  699. package/dist/assets/types.d.ts.map +0 -1
  700. package/dist/assets/types.js.map +0 -1
  701. package/dist/auth/cache.d.ts.map +0 -1
  702. package/dist/auth/cache.js.map +0 -1
  703. package/dist/auth/caching-auth-plugin.d.ts.map +0 -1
  704. package/dist/auth/caching-auth-plugin.js.map +0 -1
  705. package/dist/auth/context-helpers.d.ts.map +0 -1
  706. package/dist/auth/context-helpers.js.map +0 -1
  707. package/dist/auth/file-based-auth-cache.d.ts.map +0 -1
  708. package/dist/auth/file-based-auth-cache.js.map +0 -1
  709. package/dist/auth/index.d.ts.map +0 -1
  710. package/dist/auth/index.js.map +0 -1
  711. package/dist/auth/plugin.d.ts.map +0 -1
  712. package/dist/auth/plugin.js.map +0 -1
  713. package/dist/auth/types.d.ts.map +0 -1
  714. package/dist/auth/types.js.map +0 -1
  715. package/dist/authorization/branch.d.ts.map +0 -1
  716. package/dist/authorization/branch.js.map +0 -1
  717. package/dist/authorization/content.d.ts.map +0 -1
  718. package/dist/authorization/content.js.map +0 -1
  719. package/dist/authorization/groups/index.d.ts.map +0 -1
  720. package/dist/authorization/groups/index.js.map +0 -1
  721. package/dist/authorization/groups/loader.d.ts.map +0 -1
  722. package/dist/authorization/groups/loader.js.map +0 -1
  723. package/dist/authorization/groups/schema.d.ts.map +0 -1
  724. package/dist/authorization/groups/schema.js.map +0 -1
  725. package/dist/authorization/helpers.d.ts.map +0 -1
  726. package/dist/authorization/helpers.js.map +0 -1
  727. package/dist/authorization/index.d.ts.map +0 -1
  728. package/dist/authorization/index.js.map +0 -1
  729. package/dist/authorization/path.d.ts.map +0 -1
  730. package/dist/authorization/path.js.map +0 -1
  731. package/dist/authorization/permissions/index.d.ts.map +0 -1
  732. package/dist/authorization/permissions/index.js.map +0 -1
  733. package/dist/authorization/permissions/loader.d.ts.map +0 -1
  734. package/dist/authorization/permissions/loader.js.map +0 -1
  735. package/dist/authorization/permissions/schema.d.ts.map +0 -1
  736. package/dist/authorization/permissions/schema.js.map +0 -1
  737. package/dist/authorization/protected-branch.d.ts.map +0 -1
  738. package/dist/authorization/protected-branch.js.map +0 -1
  739. package/dist/authorization/settings-file-store.d.ts.map +0 -1
  740. package/dist/authorization/settings-file-store.js.map +0 -1
  741. package/dist/authorization/test-utils.d.ts +0 -26
  742. package/dist/authorization/test-utils.d.ts.map +0 -1
  743. package/dist/authorization/test-utils.js +0 -23
  744. package/dist/authorization/test-utils.js.map +0 -1
  745. package/dist/authorization/types.d.ts.map +0 -1
  746. package/dist/authorization/types.js.map +0 -1
  747. package/dist/authorization/validation.d.ts.map +0 -1
  748. package/dist/authorization/validation.js.map +0 -1
  749. package/dist/branch-health.d.ts.map +0 -1
  750. package/dist/branch-health.js.map +0 -1
  751. package/dist/branch-metadata.d.ts.map +0 -1
  752. package/dist/branch-metadata.js.map +0 -1
  753. package/dist/branch-registry.d.ts.map +0 -1
  754. package/dist/branch-registry.js.map +0 -1
  755. package/dist/branch-schema-cache.d.ts.map +0 -1
  756. package/dist/branch-schema-cache.js.map +0 -1
  757. package/dist/branch-workspace.d.ts.map +0 -1
  758. package/dist/branch-workspace.js.map +0 -1
  759. package/dist/build/generate-ai-content.d.ts.map +0 -1
  760. package/dist/build/generate-ai-content.js.map +0 -1
  761. package/dist/build/index.d.ts.map +0 -1
  762. package/dist/build/index.js.map +0 -1
  763. package/dist/build-mode.d.ts.map +0 -1
  764. package/dist/build-mode.js.map +0 -1
  765. package/dist/cli/cli.d.ts.map +0 -1
  766. package/dist/cli/cli.js.map +0 -1
  767. package/dist/cli/generate-ai-content.d.ts.map +0 -1
  768. package/dist/cli/generate-ai-content.js.map +0 -1
  769. package/dist/cli/init.d.ts.map +0 -1
  770. package/dist/cli/init.js.map +0 -1
  771. package/dist/cli/migrate.d.ts.map +0 -1
  772. package/dist/cli/migrate.js.map +0 -1
  773. package/dist/cli/project-detect.d.ts.map +0 -1
  774. package/dist/cli/project-detect.js.map +0 -1
  775. package/dist/cli/project-root.d.ts.map +0 -1
  776. package/dist/cli/project-root.js.map +0 -1
  777. package/dist/cli/sync.d.ts.map +0 -1
  778. package/dist/cli/sync.js.map +0 -1
  779. package/dist/cli/templates.d.ts.map +0 -1
  780. package/dist/cli/templates.js.map +0 -1
  781. package/dist/client.d.ts.map +0 -1
  782. package/dist/client.js.map +0 -1
  783. package/dist/comment-store.d.ts.map +0 -1
  784. package/dist/comment-store.js.map +0 -1
  785. package/dist/config/flatten.d.ts.map +0 -1
  786. package/dist/config/flatten.js.map +0 -1
  787. package/dist/config/helpers.d.ts.map +0 -1
  788. package/dist/config/helpers.js.map +0 -1
  789. package/dist/config/index.d.ts.map +0 -1
  790. package/dist/config/index.js.map +0 -1
  791. package/dist/config/schemas/collection.d.ts.map +0 -1
  792. package/dist/config/schemas/collection.js.map +0 -1
  793. package/dist/config/schemas/config.d.ts.map +0 -1
  794. package/dist/config/schemas/config.js.map +0 -1
  795. package/dist/config/schemas/field.d.ts.map +0 -1
  796. package/dist/config/schemas/field.js.map +0 -1
  797. package/dist/config/schemas/media.d.ts.map +0 -1
  798. package/dist/config/schemas/media.js.map +0 -1
  799. package/dist/config/schemas/permissions.d.ts.map +0 -1
  800. package/dist/config/schemas/permissions.js.map +0 -1
  801. package/dist/config/types.d.ts.map +0 -1
  802. package/dist/config/types.js.map +0 -1
  803. package/dist/config/validation.d.ts.map +0 -1
  804. package/dist/config/validation.js.map +0 -1
  805. package/dist/config-test.d.ts +0 -20
  806. package/dist/config-test.d.ts.map +0 -1
  807. package/dist/config-test.js +0 -49
  808. package/dist/config-test.js.map +0 -1
  809. package/dist/config.d.ts.map +0 -1
  810. package/dist/config.js.map +0 -1
  811. package/dist/content-id-index.d.ts.map +0 -1
  812. package/dist/content-id-index.js.map +0 -1
  813. package/dist/content-index-generation.d.ts.map +0 -1
  814. package/dist/content-index-generation.js.map +0 -1
  815. package/dist/content-index-registry.d.ts.map +0 -1
  816. package/dist/content-index-registry.js.map +0 -1
  817. package/dist/content-listing.d.ts.map +0 -1
  818. package/dist/content-listing.js.map +0 -1
  819. package/dist/content-reader.d.ts.map +0 -1
  820. package/dist/content-reader.js.map +0 -1
  821. package/dist/content-store.d.ts.map +0 -1
  822. package/dist/content-store.js.map +0 -1
  823. package/dist/content-tree.d.ts.map +0 -1
  824. package/dist/content-tree.js.map +0 -1
  825. package/dist/context.d.ts.map +0 -1
  826. package/dist/context.js.map +0 -1
  827. package/dist/dev-content-watcher.d.ts.map +0 -1
  828. package/dist/dev-content-watcher.js.map +0 -1
  829. package/dist/editor/BranchManager.d.ts.map +0 -1
  830. package/dist/editor/BranchManager.js.map +0 -1
  831. package/dist/editor/CanopyEditor.d.ts.map +0 -1
  832. package/dist/editor/CanopyEditor.js.map +0 -1
  833. package/dist/editor/CanopyEditorPage.d.ts.map +0 -1
  834. package/dist/editor/CanopyEditorPage.js.map +0 -1
  835. package/dist/editor/CommentsPanel.d.ts.map +0 -1
  836. package/dist/editor/CommentsPanel.js.map +0 -1
  837. package/dist/editor/Editor.d.ts.map +0 -1
  838. package/dist/editor/Editor.js.map +0 -1
  839. package/dist/editor/EditorPanes.d.ts.map +0 -1
  840. package/dist/editor/EditorPanes.js.map +0 -1
  841. package/dist/editor/EntryNavigator.d.ts.map +0 -1
  842. package/dist/editor/EntryNavigator.js.map +0 -1
  843. package/dist/editor/FormRenderer.d.ts.map +0 -1
  844. package/dist/editor/FormRenderer.js.map +0 -1
  845. package/dist/editor/GroupManager.d.ts.map +0 -1
  846. package/dist/editor/GroupManager.js.map +0 -1
  847. package/dist/editor/PermissionManager.d.ts.map +0 -1
  848. package/dist/editor/PermissionManager.js.map +0 -1
  849. package/dist/editor/admin/SystemHealthPanel.d.ts.map +0 -1
  850. package/dist/editor/admin/SystemHealthPanel.js.map +0 -1
  851. package/dist/editor/admin/useSystemHealth.d.ts.map +0 -1
  852. package/dist/editor/admin/useSystemHealth.js.map +0 -1
  853. package/dist/editor/canopy-path.d.ts.map +0 -1
  854. package/dist/editor/canopy-path.js.map +0 -1
  855. package/dist/editor/client-reference-resolver.d.ts.map +0 -1
  856. package/dist/editor/client-reference-resolver.js.map +0 -1
  857. package/dist/editor/comments/BranchComments.d.ts.map +0 -1
  858. package/dist/editor/comments/BranchComments.js.map +0 -1
  859. package/dist/editor/comments/EntryComments.d.ts.map +0 -1
  860. package/dist/editor/comments/EntryComments.js.map +0 -1
  861. package/dist/editor/comments/FieldWrapper.d.ts.map +0 -1
  862. package/dist/editor/comments/FieldWrapper.js.map +0 -1
  863. package/dist/editor/comments/InlineCommentThread.d.ts.map +0 -1
  864. package/dist/editor/comments/InlineCommentThread.js.map +0 -1
  865. package/dist/editor/comments/ThreadCarousel.d.ts.map +0 -1
  866. package/dist/editor/comments/ThreadCarousel.js.map +0 -1
  867. package/dist/editor/components/ConfirmDeleteModal.d.ts.map +0 -1
  868. package/dist/editor/components/ConfirmDeleteModal.js.map +0 -1
  869. package/dist/editor/components/EditorFooter.d.ts.map +0 -1
  870. package/dist/editor/components/EditorFooter.js.map +0 -1
  871. package/dist/editor/components/EditorHeader.d.ts.map +0 -1
  872. package/dist/editor/components/EditorHeader.js.map +0 -1
  873. package/dist/editor/components/EditorSidebar.d.ts.map +0 -1
  874. package/dist/editor/components/EditorSidebar.js.map +0 -1
  875. package/dist/editor/components/EntryCreateModal.d.ts.map +0 -1
  876. package/dist/editor/components/EntryCreateModal.js.map +0 -1
  877. package/dist/editor/components/RenameEntryModal.d.ts.map +0 -1
  878. package/dist/editor/components/RenameEntryModal.js.map +0 -1
  879. package/dist/editor/components/UserBadge.d.ts.map +0 -1
  880. package/dist/editor/components/UserBadge.js.map +0 -1
  881. package/dist/editor/components/index.d.ts.map +0 -1
  882. package/dist/editor/components/index.js.map +0 -1
  883. package/dist/editor/context/ApiClientContext.d.ts.map +0 -1
  884. package/dist/editor/context/ApiClientContext.js.map +0 -1
  885. package/dist/editor/context/AssetContext.d.ts.map +0 -1
  886. package/dist/editor/context/AssetContext.js.map +0 -1
  887. package/dist/editor/context/EditorStateContext.d.ts.map +0 -1
  888. package/dist/editor/context/EditorStateContext.js.map +0 -1
  889. package/dist/editor/context/SWRProvider.d.ts.map +0 -1
  890. package/dist/editor/context/SWRProvider.js.map +0 -1
  891. package/dist/editor/context/index.d.ts.map +0 -1
  892. package/dist/editor/context/index.js.map +0 -1
  893. package/dist/editor/editor-config.d.ts.map +0 -1
  894. package/dist/editor/editor-config.js.map +0 -1
  895. package/dist/editor/editor-utils.d.ts.map +0 -1
  896. package/dist/editor/editor-utils.js.map +0 -1
  897. package/dist/editor/fields/BlockField.d.ts.map +0 -1
  898. package/dist/editor/fields/BlockField.js.map +0 -1
  899. package/dist/editor/fields/CodeField.d.ts.map +0 -1
  900. package/dist/editor/fields/CodeField.js.map +0 -1
  901. package/dist/editor/fields/DateTimeField.d.ts.map +0 -1
  902. package/dist/editor/fields/DateTimeField.js.map +0 -1
  903. package/dist/editor/fields/ImageField.d.ts.map +0 -1
  904. package/dist/editor/fields/ImageField.js.map +0 -1
  905. package/dist/editor/fields/InlineGroupField.d.ts.map +0 -1
  906. package/dist/editor/fields/InlineGroupField.js.map +0 -1
  907. package/dist/editor/fields/MarkdownField.d.ts.map +0 -1
  908. package/dist/editor/fields/MarkdownField.js.map +0 -1
  909. package/dist/editor/fields/MdxImageDialog.d.ts.map +0 -1
  910. package/dist/editor/fields/MdxImageDialog.js.map +0 -1
  911. package/dist/editor/fields/NumberField.d.ts.map +0 -1
  912. package/dist/editor/fields/NumberField.js.map +0 -1
  913. package/dist/editor/fields/NumberListField.d.ts.map +0 -1
  914. package/dist/editor/fields/NumberListField.js.map +0 -1
  915. package/dist/editor/fields/ObjectField.d.ts.map +0 -1
  916. package/dist/editor/fields/ObjectField.js.map +0 -1
  917. package/dist/editor/fields/ReferenceField.d.ts.map +0 -1
  918. package/dist/editor/fields/ReferenceField.js.map +0 -1
  919. package/dist/editor/fields/SelectField.d.ts.map +0 -1
  920. package/dist/editor/fields/SelectField.js.map +0 -1
  921. package/dist/editor/fields/StringListField.d.ts.map +0 -1
  922. package/dist/editor/fields/StringListField.js.map +0 -1
  923. package/dist/editor/fields/TextField.d.ts.map +0 -1
  924. package/dist/editor/fields/TextField.js.map +0 -1
  925. package/dist/editor/fields/ToggleField.d.ts.map +0 -1
  926. package/dist/editor/fields/ToggleField.js.map +0 -1
  927. package/dist/editor/fields/entry-link/EntryLinkContext.d.ts.map +0 -1
  928. package/dist/editor/fields/entry-link/EntryLinkContext.js.map +0 -1
  929. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts.map +0 -1
  930. package/dist/editor/fields/entry-link/InsertEntryLink.js.map +0 -1
  931. package/dist/editor/fields/entry-link/index.d.ts.map +0 -1
  932. package/dist/editor/fields/entry-link/index.js.map +0 -1
  933. package/dist/editor/group-manager/ExternalGroupsTab.d.ts.map +0 -1
  934. package/dist/editor/group-manager/ExternalGroupsTab.js.map +0 -1
  935. package/dist/editor/group-manager/GroupCard.d.ts.map +0 -1
  936. package/dist/editor/group-manager/GroupCard.js.map +0 -1
  937. package/dist/editor/group-manager/GroupForm.d.ts.map +0 -1
  938. package/dist/editor/group-manager/GroupForm.js.map +0 -1
  939. package/dist/editor/group-manager/InternalGroupsTab.d.ts.map +0 -1
  940. package/dist/editor/group-manager/InternalGroupsTab.js.map +0 -1
  941. package/dist/editor/group-manager/MemberList.d.ts.map +0 -1
  942. package/dist/editor/group-manager/MemberList.js.map +0 -1
  943. package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts.map +0 -1
  944. package/dist/editor/group-manager/hooks/useExternalGroupSearch.js.map +0 -1
  945. package/dist/editor/group-manager/hooks/useGroupState.d.ts.map +0 -1
  946. package/dist/editor/group-manager/hooks/useGroupState.js.map +0 -1
  947. package/dist/editor/group-manager/hooks/useUserSearch.d.ts.map +0 -1
  948. package/dist/editor/group-manager/hooks/useUserSearch.js.map +0 -1
  949. package/dist/editor/group-manager/index.d.ts.map +0 -1
  950. package/dist/editor/group-manager/index.js.map +0 -1
  951. package/dist/editor/group-manager/types.d.ts.map +0 -1
  952. package/dist/editor/group-manager/types.js.map +0 -1
  953. package/dist/editor/hooks/__test__/test-utils.d.ts +0 -164
  954. package/dist/editor/hooks/__test__/test-utils.d.ts.map +0 -1
  955. package/dist/editor/hooks/__test__/test-utils.js +0 -203
  956. package/dist/editor/hooks/__test__/test-utils.js.map +0 -1
  957. package/dist/editor/hooks/index.d.ts.map +0 -1
  958. package/dist/editor/hooks/index.js.map +0 -1
  959. package/dist/editor/hooks/useBranchActions.d.ts.map +0 -1
  960. package/dist/editor/hooks/useBranchActions.js.map +0 -1
  961. package/dist/editor/hooks/useBranchManager.d.ts.map +0 -1
  962. package/dist/editor/hooks/useBranchManager.js.map +0 -1
  963. package/dist/editor/hooks/useBranchesData.d.ts.map +0 -1
  964. package/dist/editor/hooks/useBranchesData.js.map +0 -1
  965. package/dist/editor/hooks/useCommentSystem.d.ts.map +0 -1
  966. package/dist/editor/hooks/useCommentSystem.js.map +0 -1
  967. package/dist/editor/hooks/useCommentsData.d.ts.map +0 -1
  968. package/dist/editor/hooks/useCommentsData.js.map +0 -1
  969. package/dist/editor/hooks/useDraftManager.d.ts.map +0 -1
  970. package/dist/editor/hooks/useDraftManager.js.map +0 -1
  971. package/dist/editor/hooks/useEditorLayout.d.ts.map +0 -1
  972. package/dist/editor/hooks/useEditorLayout.js.map +0 -1
  973. package/dist/editor/hooks/useEntriesData.d.ts.map +0 -1
  974. package/dist/editor/hooks/useEntriesData.js.map +0 -1
  975. package/dist/editor/hooks/useEntryLinkResolution.d.ts.map +0 -1
  976. package/dist/editor/hooks/useEntryLinkResolution.js.map +0 -1
  977. package/dist/editor/hooks/useEntryManager.d.ts.map +0 -1
  978. package/dist/editor/hooks/useEntryManager.js.map +0 -1
  979. package/dist/editor/hooks/useGroupManager.d.ts.map +0 -1
  980. package/dist/editor/hooks/useGroupManager.js.map +0 -1
  981. package/dist/editor/hooks/usePermissionManager.d.ts.map +0 -1
  982. package/dist/editor/hooks/usePermissionManager.js.map +0 -1
  983. package/dist/editor/hooks/useReferenceResolution.d.ts.map +0 -1
  984. package/dist/editor/hooks/useReferenceResolution.js.map +0 -1
  985. package/dist/editor/hooks/useSchemaManager.d.ts.map +0 -1
  986. package/dist/editor/hooks/useSchemaManager.js.map +0 -1
  987. package/dist/editor/hooks/useUserContext.d.ts.map +0 -1
  988. package/dist/editor/hooks/useUserContext.js.map +0 -1
  989. package/dist/editor/hooks/useUserMetadata.d.ts.map +0 -1
  990. package/dist/editor/hooks/useUserMetadata.js.map +0 -1
  991. package/dist/editor/media/AssetCard.d.ts.map +0 -1
  992. package/dist/editor/media/AssetCard.js.map +0 -1
  993. package/dist/editor/media/CropStep.d.ts.map +0 -1
  994. package/dist/editor/media/CropStep.js.map +0 -1
  995. package/dist/editor/media/MediaLibrary.d.ts.map +0 -1
  996. package/dist/editor/media/MediaLibrary.js.map +0 -1
  997. package/dist/editor/media/MediaLibraryBody.d.ts.map +0 -1
  998. package/dist/editor/media/MediaLibraryBody.js.map +0 -1
  999. package/dist/editor/media/crop-math.d.ts.map +0 -1
  1000. package/dist/editor/media/crop-math.js.map +0 -1
  1001. package/dist/editor/media/upload-asset.d.ts.map +0 -1
  1002. package/dist/editor/media/upload-asset.js.map +0 -1
  1003. package/dist/editor/media/upload-constants.d.ts.map +0 -1
  1004. package/dist/editor/media/upload-constants.js.map +0 -1
  1005. package/dist/editor/media/useAssetUpload.d.ts.map +0 -1
  1006. package/dist/editor/media/useAssetUpload.js.map +0 -1
  1007. package/dist/editor/media/xhr-upload.d.ts.map +0 -1
  1008. package/dist/editor/media/xhr-upload.js.map +0 -1
  1009. package/dist/editor/permission-manager/GroupSelector.d.ts.map +0 -1
  1010. package/dist/editor/permission-manager/GroupSelector.js.map +0 -1
  1011. package/dist/editor/permission-manager/PermissionEditor.d.ts.map +0 -1
  1012. package/dist/editor/permission-manager/PermissionEditor.js.map +0 -1
  1013. package/dist/editor/permission-manager/PermissionLevelBadge.d.ts.map +0 -1
  1014. package/dist/editor/permission-manager/PermissionLevelBadge.js.map +0 -1
  1015. package/dist/editor/permission-manager/PermissionTree.d.ts.map +0 -1
  1016. package/dist/editor/permission-manager/PermissionTree.js.map +0 -1
  1017. package/dist/editor/permission-manager/UserSelector.d.ts.map +0 -1
  1018. package/dist/editor/permission-manager/UserSelector.js.map +0 -1
  1019. package/dist/editor/permission-manager/constants.d.ts.map +0 -1
  1020. package/dist/editor/permission-manager/constants.js.map +0 -1
  1021. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts.map +0 -1
  1022. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js.map +0 -1
  1023. package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts.map +0 -1
  1024. package/dist/editor/permission-manager/hooks/usePermissionTree.js.map +0 -1
  1025. package/dist/editor/permission-manager/index.d.ts.map +0 -1
  1026. package/dist/editor/permission-manager/index.js.map +0 -1
  1027. package/dist/editor/permission-manager/types.d.ts.map +0 -1
  1028. package/dist/editor/permission-manager/types.js.map +0 -1
  1029. package/dist/editor/permission-manager/utils.d.ts.map +0 -1
  1030. package/dist/editor/permission-manager/utils.js.map +0 -1
  1031. package/dist/editor/preview-bridge.d.ts.map +0 -1
  1032. package/dist/editor/preview-bridge.js.map +0 -1
  1033. package/dist/editor/relative-time.d.ts.map +0 -1
  1034. package/dist/editor/relative-time.js.map +0 -1
  1035. package/dist/editor/schema-editor/CollectionEditor.d.ts.map +0 -1
  1036. package/dist/editor/schema-editor/CollectionEditor.js.map +0 -1
  1037. package/dist/editor/schema-editor/EntryTypeEditor.d.ts.map +0 -1
  1038. package/dist/editor/schema-editor/EntryTypeEditor.js.map +0 -1
  1039. package/dist/editor/schema-editor/index.d.ts.map +0 -1
  1040. package/dist/editor/schema-editor/index.js.map +0 -1
  1041. package/dist/editor/setup-test-dom.d.ts +0 -1
  1042. package/dist/editor/setup-test-dom.d.ts.map +0 -1
  1043. package/dist/editor/setup-test-dom.js +0 -12
  1044. package/dist/editor/setup-test-dom.js.map +0 -1
  1045. package/dist/editor/test-setup.d.ts +0 -6
  1046. package/dist/editor/test-setup.d.ts.map +0 -1
  1047. package/dist/editor/test-setup.js +0 -42
  1048. package/dist/editor/test-setup.js.map +0 -1
  1049. package/dist/editor/theme.d.ts.map +0 -1
  1050. package/dist/editor/theme.js.map +0 -1
  1051. package/dist/editor/utils/env.d.ts.map +0 -1
  1052. package/dist/editor/utils/env.js.map +0 -1
  1053. package/dist/entry-link-resolver.d.ts.map +0 -1
  1054. package/dist/entry-link-resolver.js.map +0 -1
  1055. package/dist/entry-schema-registry.d.ts.map +0 -1
  1056. package/dist/entry-schema-registry.js.map +0 -1
  1057. package/dist/entry-schema.d.ts.map +0 -1
  1058. package/dist/entry-schema.js.map +0 -1
  1059. package/dist/git-manager.d.ts.map +0 -1
  1060. package/dist/git-manager.js.map +0 -1
  1061. package/dist/github-service.d.ts.map +0 -1
  1062. package/dist/github-service.js.map +0 -1
  1063. package/dist/http/handler.d.ts.map +0 -1
  1064. package/dist/http/handler.js.map +0 -1
  1065. package/dist/http/index.d.ts.map +0 -1
  1066. package/dist/http/index.js.map +0 -1
  1067. package/dist/http/router.d.ts.map +0 -1
  1068. package/dist/http/router.js.map +0 -1
  1069. package/dist/http/types.d.ts.map +0 -1
  1070. package/dist/http/types.js.map +0 -1
  1071. package/dist/id.d.ts.map +0 -1
  1072. package/dist/id.js.map +0 -1
  1073. package/dist/index.d.ts.map +0 -1
  1074. package/dist/index.js.map +0 -1
  1075. package/dist/operating-mode/client-safe-strategy.d.ts.map +0 -1
  1076. package/dist/operating-mode/client-safe-strategy.js.map +0 -1
  1077. package/dist/operating-mode/client-unsafe-strategy.d.ts.map +0 -1
  1078. package/dist/operating-mode/client-unsafe-strategy.js.map +0 -1
  1079. package/dist/operating-mode/client.d.ts.map +0 -1
  1080. package/dist/operating-mode/client.js.map +0 -1
  1081. package/dist/operating-mode/deployment-name-fixtures.d.ts.map +0 -1
  1082. package/dist/operating-mode/deployment-name-fixtures.js.map +0 -1
  1083. package/dist/operating-mode/deployment-name.d.ts.map +0 -1
  1084. package/dist/operating-mode/deployment-name.js.map +0 -1
  1085. package/dist/operating-mode/index.d.ts.map +0 -1
  1086. package/dist/operating-mode/index.js.map +0 -1
  1087. package/dist/operating-mode/mode-env.d.ts.map +0 -1
  1088. package/dist/operating-mode/mode-env.js.map +0 -1
  1089. package/dist/operating-mode/types.d.ts.map +0 -1
  1090. package/dist/operating-mode/types.js.map +0 -1
  1091. package/dist/paths/branch-name.d.ts.map +0 -1
  1092. package/dist/paths/branch-name.js.map +0 -1
  1093. package/dist/paths/branch.d.ts.map +0 -1
  1094. package/dist/paths/branch.js.map +0 -1
  1095. package/dist/paths/index.d.ts.map +0 -1
  1096. package/dist/paths/index.js.map +0 -1
  1097. package/dist/paths/normalize-server.d.ts.map +0 -1
  1098. package/dist/paths/normalize-server.js.map +0 -1
  1099. package/dist/paths/normalize.d.ts.map +0 -1
  1100. package/dist/paths/normalize.js.map +0 -1
  1101. package/dist/paths/resolve.d.ts.map +0 -1
  1102. package/dist/paths/resolve.js.map +0 -1
  1103. package/dist/paths/test-utils.d.ts +0 -22
  1104. package/dist/paths/test-utils.d.ts.map +0 -1
  1105. package/dist/paths/test-utils.js +0 -21
  1106. package/dist/paths/test-utils.js.map +0 -1
  1107. package/dist/paths/types.d.ts.map +0 -1
  1108. package/dist/paths/types.js.map +0 -1
  1109. package/dist/paths/validation.d.ts.map +0 -1
  1110. package/dist/paths/validation.js.map +0 -1
  1111. package/dist/reference-resolver.d.ts.map +0 -1
  1112. package/dist/reference-resolver.js.map +0 -1
  1113. package/dist/resolve-canopy-user.d.ts.map +0 -1
  1114. package/dist/resolve-canopy-user.js.map +0 -1
  1115. package/dist/resource-generation.d.ts.map +0 -1
  1116. package/dist/resource-generation.js.map +0 -1
  1117. package/dist/schema/index.d.ts.map +0 -1
  1118. package/dist/schema/index.js.map +0 -1
  1119. package/dist/schema/meta-loader.d.ts.map +0 -1
  1120. package/dist/schema/meta-loader.js.map +0 -1
  1121. package/dist/schema/resolver.d.ts.map +0 -1
  1122. package/dist/schema/resolver.js.map +0 -1
  1123. package/dist/schema/schema-store-types.d.ts.map +0 -1
  1124. package/dist/schema/schema-store-types.js.map +0 -1
  1125. package/dist/schema/schema-store.d.ts.map +0 -1
  1126. package/dist/schema/schema-store.js.map +0 -1
  1127. package/dist/schema/types.d.ts.map +0 -1
  1128. package/dist/schema/types.js.map +0 -1
  1129. package/dist/server.d.ts.map +0 -1
  1130. package/dist/server.js.map +0 -1
  1131. package/dist/services.d.ts.map +0 -1
  1132. package/dist/services.js.map +0 -1
  1133. package/dist/settings-workspace.d.ts.map +0 -1
  1134. package/dist/settings-workspace.js.map +0 -1
  1135. package/dist/static/index.d.ts.map +0 -1
  1136. package/dist/static/index.js.map +0 -1
  1137. package/dist/sync-core.d.ts.map +0 -1
  1138. package/dist/sync-core.js.map +0 -1
  1139. package/dist/task-queue/index.d.ts.map +0 -1
  1140. package/dist/task-queue/index.js.map +0 -1
  1141. package/dist/task-queue/task-queue.d.ts.map +0 -1
  1142. package/dist/task-queue/task-queue.js.map +0 -1
  1143. package/dist/task-queue/types.d.ts.map +0 -1
  1144. package/dist/task-queue/types.js.map +0 -1
  1145. package/dist/types.d.ts.map +0 -1
  1146. package/dist/types.js.map +0 -1
  1147. package/dist/url-path-resolver.d.ts.map +0 -1
  1148. package/dist/url-path-resolver.js.map +0 -1
  1149. package/dist/user.d.ts.map +0 -1
  1150. package/dist/user.js.map +0 -1
  1151. package/dist/utils/async-mutex.d.ts.map +0 -1
  1152. package/dist/utils/async-mutex.js.map +0 -1
  1153. package/dist/utils/atomic-write.d.ts.map +0 -1
  1154. package/dist/utils/atomic-write.js.map +0 -1
  1155. package/dist/utils/body-field.d.ts.map +0 -1
  1156. package/dist/utils/body-field.js.map +0 -1
  1157. package/dist/utils/content-write-lock.d.ts.map +0 -1
  1158. package/dist/utils/content-write-lock.js.map +0 -1
  1159. package/dist/utils/debug.d.ts.map +0 -1
  1160. package/dist/utils/debug.js.map +0 -1
  1161. package/dist/utils/entry-url.d.ts.map +0 -1
  1162. package/dist/utils/entry-url.js.map +0 -1
  1163. package/dist/utils/error.d.ts.map +0 -1
  1164. package/dist/utils/error.js.map +0 -1
  1165. package/dist/utils/flatten-group-fields.d.ts.map +0 -1
  1166. package/dist/utils/flatten-group-fields.js.map +0 -1
  1167. package/dist/utils/format.d.ts.map +0 -1
  1168. package/dist/utils/format.js.map +0 -1
  1169. package/dist/utils/fs.d.ts.map +0 -1
  1170. package/dist/utils/fs.js.map +0 -1
  1171. package/dist/utils/git.d.ts.map +0 -1
  1172. package/dist/utils/git.js.map +0 -1
  1173. package/dist/utils/logger.d.ts.map +0 -1
  1174. package/dist/utils/logger.js.map +0 -1
  1175. package/dist/utils/occ-json-write.d.ts.map +0 -1
  1176. package/dist/utils/occ-json-write.js.map +0 -1
  1177. package/dist/utils/provisioning-lock.d.ts.map +0 -1
  1178. package/dist/utils/provisioning-lock.js.map +0 -1
  1179. package/dist/utils/sanitize-href.d.ts.map +0 -1
  1180. package/dist/utils/sanitize-href.js.map +0 -1
  1181. package/dist/utils/title-field.d.ts.map +0 -1
  1182. package/dist/utils/title-field.js.map +0 -1
  1183. package/dist/validation/deletion-checker.d.ts.map +0 -1
  1184. package/dist/validation/deletion-checker.js.map +0 -1
  1185. package/dist/validation/entry-link-validator.d.ts.map +0 -1
  1186. package/dist/validation/entry-link-validator.js.map +0 -1
  1187. package/dist/validation/entry-type-reference-validator.d.ts.map +0 -1
  1188. package/dist/validation/entry-type-reference-validator.js.map +0 -1
  1189. package/dist/validation/entry-validator.d.ts.map +0 -1
  1190. package/dist/validation/entry-validator.js.map +0 -1
  1191. package/dist/validation/field-traversal.d.ts.map +0 -1
  1192. package/dist/validation/field-traversal.js.map +0 -1
  1193. package/dist/validation/reference-validator.d.ts.map +0 -1
  1194. package/dist/validation/reference-validator.js.map +0 -1
  1195. package/dist/worker/cms-worker.d.ts.map +0 -1
  1196. package/dist/worker/cms-worker.js.map +0 -1
  1197. package/dist/worker/log.d.ts.map +0 -1
  1198. package/dist/worker/log.js.map +0 -1
  1199. package/dist/worker/task-queue-config.d.ts.map +0 -1
  1200. package/dist/worker/task-queue-config.js.map +0 -1
  1201. package/dist/worker/task-queue.d.ts.map +0 -1
  1202. package/dist/worker/task-queue.js.map +0 -1
  1203. package/dist/worker/worker-status.d.ts.map +0 -1
  1204. package/dist/worker/worker-status.js.map +0 -1
package/dist/cli/cli.js CHANGED
@@ -1047,7 +1047,7 @@ CanopyCMS worker run-once (mode: ${mode}, auth: ${authMode})
1047
1047
  }
1048
1048
  console.log("\nDone");
1049
1049
  }
1050
- var __defProp2, __getOwnPropNames2, __esm2, __export2, active, init_logger, primitiveFieldTypes, fieldTypes, init_types, fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, ASPECT_RATIO_RE, imageFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema, init_field, relativePathSchema, entryTypeSchema, collectionSchema, init_collection, mediaSchema, init_media, defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE, init_config, permissionTargetSchema, pathPermissionSchema, init_permissions, init_normalize, init_types2, init_flatten, init_mode_env, init_validation, init_helpers, init_config2, init_error2, init_fs2, templates_exports, __dirname, TEMPLATES_DIR, SCAFFOLD_STACK_NAME, init_templates, project_detect_exports, COMMANDS, MAX_WORKSPACE_DEPTH, CDK_DEPENDENCIES, init_project_detect, task_queue_config_exports, init_task_queue_config, LOG_LEVELS, DebugLogger, testLogger, init_debug, init_atomic_write, DEFAULT_MAX_RETRIES, nullLogger, init_task_queue, init_task_queue2, task_queue_exports, debugLogger, cmsTaskQueueLogger, init_task_queue3, ProdClientSafeStrategy, DevClientSafeStrategy, warned, VALID_DEPLOYMENT_NAME, isValidDeploymentName, ProdStrategy, DevStrategy, strategyCache;
1050
+ var __defProp2, __getOwnPropNames2, __esm2, __export2, active, init_logger, primitiveFieldTypes, fieldTypes, init_types, fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, ASPECT_RATIO_RE, imageFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema, init_field, relativePathSchema, entryTypeSchema, collectionSchema, init_collection, mediaSchema, init_media, defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, basePathSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE, init_config, permissionTargetSchema, pathPermissionSchema, init_permissions, init_normalize, init_types2, init_flatten, init_mode_env, init_validation, init_helpers, init_config2, init_error2, init_fs2, templates_exports, __dirname, TEMPLATES_DIR, SCAFFOLD_STACK_NAME, init_templates, project_detect_exports, COMMANDS, MAX_WORKSPACE_DEPTH, CDK_DEPENDENCIES, init_project_detect, task_queue_config_exports, init_task_queue_config, LOG_LEVELS, DebugLogger, testLogger, init_debug, init_atomic_write, DEFAULT_MAX_RETRIES, nullLogger, init_task_queue, init_task_queue2, task_queue_exports, debugLogger, cmsTaskQueueLogger, init_task_queue3, ProdClientSafeStrategy, DevClientSafeStrategy, warned, VALID_DEPLOYMENT_NAME, isValidDeploymentName, ProdStrategy, DevStrategy, strategyCache;
1051
1051
  var init_init = __esm({
1052
1052
  "dist/cli/init.js"() {
1053
1053
  "use strict";
@@ -1134,7 +1134,12 @@ var init_init = __esm({
1134
1134
  collections: z.array(z.string().min(1)).min(1).optional(),
1135
1135
  entryTypes: z.array(z.string().min(1)).min(1).optional(),
1136
1136
  displayField: z.string().min(1).optional(),
1137
- options: z.array(referenceOptionSchema).optional()
1137
+ options: z.array(referenceOptionSchema).optional(),
1138
+ // Must be listed here, not just on ReferenceFieldConfig: zod strips unknown keys by
1139
+ // default, so a consumer that adopts this schema's parse output would silently delete a
1140
+ // runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
1141
+ // absent on purpose — it is type-inference-only and stripping it changes no behavior.)
1142
+ includeBody: z.boolean().optional()
1138
1143
  });
1139
1144
  fieldHolder = [z.never()];
1140
1145
  blockSchema = z.object({
@@ -1255,6 +1260,9 @@ var init_init = __esm({
1255
1260
  deployedAsSchema = z4.enum(["static", "server"]).default("server");
1256
1261
  contentRootSchema = relativePathSchema.default("content");
1257
1262
  sourceRootSchema = z4.string().min(1).optional();
1263
+ basePathSchema = z4.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
1264
+ message: "basePath must not be empty or only whitespace"
1265
+ }).optional();
1258
1266
  deploymentNameSchema = z4.string().default("prod");
1259
1267
  devContentSyncSchema = z4.enum(["off", "warn"]).default("warn");
1260
1268
  devConfigSchema = z4.object({
@@ -1317,6 +1325,7 @@ var init_init = __esm({
1317
1325
  deploymentName: deploymentNameSchema.optional(),
1318
1326
  contentRoot: contentRootSchema.default("content"),
1319
1327
  sourceRoot: sourceRootSchema.optional(),
1328
+ basePath: basePathSchema,
1320
1329
  editor: editorConfigSchema.optional(),
1321
1330
  authPlugin: z4.custom().optional(),
1322
1331
  entryLinkUrl: z4.custom().optional(),
@@ -1480,6 +1489,7 @@ var init_init = __esm({
1480
1489
  };
1481
1490
  MAX_WORKSPACE_DEPTH = 8;
1482
1491
  CDK_DEPENDENCIES = [
1492
+ "canopycms",
1483
1493
  "canopycms-cdk",
1484
1494
  "aws-cdk-lib",
1485
1495
  "constructs",
@@ -1844,63 +1854,73 @@ import { isAbsolute as isAbsolute2 } from "pathe";
1844
1854
  import { z as z42 } from "zod";
1845
1855
  import { z as z52 } from "zod";
1846
1856
  import { z as z6 } from "zod";
1847
- import path82 from "node:path";
1848
- import path19 from "node:path";
1857
+ import path9 from "node:path";
1858
+ import path20 from "node:path";
1849
1859
  import { createJiti as createJiti2 } from "jiti";
1850
- import fs16 from "node:fs/promises";
1851
- import path18 from "node:path";
1852
- import fs9 from "node:fs/promises";
1860
+ import fs17 from "node:fs/promises";
1861
+ import path19 from "node:path";
1862
+ import fs10 from "node:fs/promises";
1863
+ import path11 from "node:path";
1864
+ import matter2 from "gray-matter";
1865
+ import { parse as yamlParse } from "yaml";
1866
+ import fs7 from "node:fs/promises";
1853
1867
  import path10 from "node:path";
1854
1868
  import matter from "gray-matter";
1855
- import { parse as yamlParse, stringify as yamlStringify } from "yaml";
1856
- import fs7 from "node:fs/promises";
1857
- import path9 from "node:path";
1869
+ import { isCollection, isMap, isNode, isScalar, isSeq, parseDocument, stringify as yamlStringify } from "yaml";
1858
1870
  import path32 from "node:path";
1859
1871
  import fs23 from "node:fs/promises";
1860
1872
  import path22 from "node:path";
1861
1873
  import lockfile from "proper-lockfile";
1874
+ import fs42 from "node:fs/promises";
1875
+ import path52 from "node:path";
1862
1876
  import fs32 from "node:fs/promises";
1863
1877
  import path42 from "node:path";
1864
1878
  import { generate } from "short-uuid";
1865
- import path52 from "node:path";
1866
- import fs42 from "node:fs/promises";
1867
1879
  import path62 from "node:path";
1868
- import { randomUUID } from "node:crypto";
1869
- import fs62 from "node:fs/promises";
1880
+ import fs52 from "node:fs/promises";
1870
1881
  import path72 from "node:path";
1871
- import { promises as fs52 } from "fs";
1882
+ import { randomUUID } from "node:crypto";
1883
+ import fs72 from "node:fs/promises";
1884
+ import path82 from "node:path";
1885
+ import { promises as fs62 } from "fs";
1872
1886
  import { join as join3 } from "pathe";
1873
1887
  import { z as z7 } from "zod";
1874
1888
  import chokidar from "chokidar";
1875
- import fs72 from "node:fs/promises";
1876
- import { sep, resolve, relative } from "node:path";
1877
1889
  import fs8 from "node:fs/promises";
1878
- import path92 from "node:path";
1879
- import path11 from "node:path";
1880
- import fs10 from "node:fs/promises";
1881
- import { minimatch } from "minimatch";
1882
- import path16 from "node:path";
1883
- import fs13 from "node:fs/promises";
1884
- import path14 from "node:path";
1885
- import fs11 from "node:fs/promises";
1890
+ import { sep, resolve, relative } from "node:path";
1891
+ import fs9 from "node:fs/promises";
1892
+ import path102 from "node:path";
1886
1893
  import path12 from "node:path";
1894
+ import fs11 from "node:fs/promises";
1895
+ import { minimatch } from "minimatch";
1896
+ import path17 from "node:path";
1897
+ import fs14 from "node:fs/promises";
1898
+ import path15 from "node:path";
1887
1899
  import fs12 from "node:fs/promises";
1888
1900
  import path13 from "node:path";
1901
+ import fs13 from "node:fs/promises";
1902
+ import path14 from "node:path";
1889
1903
  import { randomUUID as randomUUID2 } from "node:crypto";
1890
1904
  import lockfile2 from "proper-lockfile";
1891
- import fs14 from "node:fs/promises";
1892
- import path15 from "node:path";
1905
+ import fs15 from "node:fs/promises";
1906
+ import path16 from "node:path";
1893
1907
  import { simpleGit as simpleGit2 } from "simple-git";
1894
1908
  import { simpleGit as simpleGit3 } from "simple-git";
1895
- import fs15 from "node:fs/promises";
1896
- import path17 from "node:path";
1897
- import matter2 from "gray-matter";
1909
+ import fs16 from "node:fs/promises";
1910
+ import path18 from "node:path";
1911
+ import matter3 from "gray-matter";
1898
1912
  import { parse as yamlParse2 } from "yaml";
1899
- function normalizeFilesystemPath(path202) {
1900
- return path202.split(/[\\/]+/).filter(Boolean).join("/");
1913
+ function canopyLogWarn2(...args) {
1914
+ active2.warn(...args);
1915
+ }
1916
+ function canopyLogError(...args) {
1917
+ active2.error(...args);
1918
+ }
1919
+ function normalizeFilesystemPath(path212) {
1920
+ return path212.split(/[\\/]+/).filter(Boolean).join("/");
1901
1921
  }
1902
- function hasTraversalSequence(path202) {
1903
- const normalized = normalizeFilesystemPath(path202);
1922
+ function hasTraversalSequence(path212) {
1923
+ const normalized = normalizeFilesystemPath(path212);
1904
1924
  return normalized.includes("..");
1905
1925
  }
1906
1926
  function createLogicalPath(...segments) {
@@ -1910,20 +1930,14 @@ function createLogicalPath(...segments) {
1910
1930
  }
1911
1931
  return normalized;
1912
1932
  }
1913
- function trimSlashes(path202) {
1933
+ function trimSlashes(path212) {
1914
1934
  let start = 0;
1915
- let end = path202.length;
1916
- while (start < end && path202[start] === "/")
1935
+ let end = path212.length;
1936
+ while (start < end && path212[start] === "/")
1917
1937
  start++;
1918
- while (end > start && path202[end - 1] === "/")
1938
+ while (end > start && path212[end - 1] === "/")
1919
1939
  end--;
1920
- return path202.slice(start, end);
1921
- }
1922
- function canopyLogWarn2(...args) {
1923
- active2.warn(...args);
1924
- }
1925
- function canopyLogError(...args) {
1926
- active2.error(...args);
1940
+ return path212.slice(start, end);
1927
1941
  }
1928
1942
  function readModeEnv() {
1929
1943
  if (typeof process === "undefined" || typeof process.env === "undefined")
@@ -2007,8 +2021,26 @@ function operatingStrategy2(mode) {
2007
2021
  function clearStrategyCache() {
2008
2022
  strategyCache2.clear();
2009
2023
  }
2024
+ function getErrorMessage3(err) {
2025
+ if (err instanceof Error) {
2026
+ return err.message;
2027
+ }
2028
+ if (typeof err === "string") {
2029
+ return err;
2030
+ }
2031
+ return String(err);
2032
+ }
2033
+ function isNodeError3(err) {
2034
+ return err instanceof Error && "code" in err;
2035
+ }
2036
+ function isNotFoundError3(err) {
2037
+ return isNodeError3(err) && err.code === "ENOENT";
2038
+ }
2039
+ function isFileExistsError2(err) {
2040
+ return isNodeError3(err) && err.code === "EEXIST";
2041
+ }
2010
2042
  async function atomicWriteFile2(filePath, content) {
2011
- const dir = path9.dirname(filePath);
2043
+ const dir = path10.dirname(filePath);
2012
2044
  await fs7.mkdir(dir, { recursive: true });
2013
2045
  const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
2014
2046
  await fs7.writeFile(tempPath, content, typeof content === "string" ? "utf-8" : void 0);
@@ -2020,6 +2052,196 @@ async function atomicWriteFile2(filePath, content) {
2020
2052
  throw err;
2021
2053
  }
2022
2054
  }
2055
+ function isBlockStructuralKey(key) {
2056
+ return BLOCK_STRUCTURAL_KEYS.has(key);
2057
+ }
2058
+ function isPlainRecord(value) {
2059
+ if (typeof value !== "object" || value === null || Array.isArray(value))
2060
+ return false;
2061
+ const proto = Object.getPrototypeOf(value);
2062
+ return proto === Object.prototype || proto === null;
2063
+ }
2064
+ function recordKeyOf(keyNode) {
2065
+ if (!isScalar(keyNode))
2066
+ return void 0;
2067
+ const { value } = keyNode;
2068
+ if (value === null || value === void 0)
2069
+ return void 0;
2070
+ if (typeof value === "object")
2071
+ return void 0;
2072
+ return String(value);
2073
+ }
2074
+ function carryComments(from, to) {
2075
+ if (!isNode(from) || !isNode(to))
2076
+ return;
2077
+ const source = from;
2078
+ const target = to;
2079
+ if (source.commentBefore !== void 0)
2080
+ target.commentBefore = source.commentBefore;
2081
+ if (source.comment !== void 0)
2082
+ target.comment = source.comment;
2083
+ if (source.spaceBefore !== void 0)
2084
+ target.spaceBefore = source.spaceBefore;
2085
+ }
2086
+ function identityKey(value) {
2087
+ try {
2088
+ return JSON.stringify(value) ?? "undefined";
2089
+ } catch {
2090
+ return void 0;
2091
+ }
2092
+ }
2093
+ function nodePlainValue(node) {
2094
+ if (!isNode(node))
2095
+ return node;
2096
+ try {
2097
+ return node.toJSON();
2098
+ } catch {
2099
+ return void 0;
2100
+ }
2101
+ }
2102
+ function nodeIdentityKey(node) {
2103
+ const plain = nodePlainValue(node);
2104
+ return plain === void 0 && isNode(node) ? void 0 : identityKey(plain);
2105
+ }
2106
+ function sharesFieldEvidence(existing, value, depth) {
2107
+ for (const key of Object.keys(value)) {
2108
+ if (isBlockStructuralKey(key))
2109
+ continue;
2110
+ if (!Object.prototype.hasOwnProperty.call(existing, key))
2111
+ continue;
2112
+ const before = existing[key];
2113
+ const after = value[key];
2114
+ if (depth < EVIDENCE_MAX_DEPTH && isPlainRecord(before) && isPlainRecord(after) && sharesFieldEvidence(before, after, depth + 1)) {
2115
+ return true;
2116
+ }
2117
+ const beforeKey = identityKey(before);
2118
+ if (beforeKey !== void 0 && beforeKey === identityKey(after))
2119
+ return true;
2120
+ }
2121
+ return false;
2122
+ }
2123
+ function looksLikeSameItem(node, value) {
2124
+ const existing = nodePlainValue(node);
2125
+ if (!isPlainRecord(existing) || !isPlainRecord(value))
2126
+ return true;
2127
+ return sharesFieldEvidence(existing, value, 0);
2128
+ }
2129
+ function reconcileNode(doc, existing, value) {
2130
+ if (isMap(existing) && isPlainRecord(value)) {
2131
+ reconcileMap(doc, existing, value);
2132
+ return existing;
2133
+ }
2134
+ if (isSeq(existing) && Array.isArray(value)) {
2135
+ reconcileSeq(doc, existing, value);
2136
+ return existing;
2137
+ }
2138
+ if (isScalar(existing) && Object.is(existing.value, value))
2139
+ return existing;
2140
+ const fresh = doc.createNode(value);
2141
+ if (!isCollection(existing))
2142
+ carryComments(existing, fresh);
2143
+ return fresh;
2144
+ }
2145
+ function reconcileMap(doc, map, value) {
2146
+ const wanted = new Set(Object.keys(value).filter((key) => value[key] !== void 0));
2147
+ const seen = /* @__PURE__ */ new Set();
2148
+ const retained = [];
2149
+ for (const pair of map.items) {
2150
+ const key = recordKeyOf(pair.key);
2151
+ if (key === void 0 || !wanted.has(key) || seen.has(key))
2152
+ continue;
2153
+ seen.add(key);
2154
+ pair.value = reconcileNode(doc, pair.value, value[key]);
2155
+ retained.push(pair);
2156
+ }
2157
+ map.items = retained;
2158
+ for (const key of Object.keys(value)) {
2159
+ if (seen.has(key) || !wanted.has(key))
2160
+ continue;
2161
+ map.set(doc.createNode(key), doc.createNode(value[key]));
2162
+ }
2163
+ }
2164
+ function reconcileSeq(doc, seq, value) {
2165
+ const oldItems = seq.items;
2166
+ const byIdentity = /* @__PURE__ */ new Map();
2167
+ oldItems.forEach((node, index) => {
2168
+ const key = nodeIdentityKey(node);
2169
+ if (key === void 0)
2170
+ return;
2171
+ const bucket = byIdentity.get(key);
2172
+ if (bucket)
2173
+ bucket.push(index);
2174
+ else
2175
+ byIdentity.set(key, [index]);
2176
+ });
2177
+ const consumed = /* @__PURE__ */ new Set();
2178
+ const matches = value.map((item) => {
2179
+ const key = identityKey(item);
2180
+ if (key === void 0)
2181
+ return void 0;
2182
+ const bucket = byIdentity.get(key);
2183
+ while (bucket && bucket.length > 0) {
2184
+ const index = bucket.shift();
2185
+ if (index !== void 0 && !consumed.has(index)) {
2186
+ consumed.add(index);
2187
+ return index;
2188
+ }
2189
+ }
2190
+ return void 0;
2191
+ });
2192
+ seq.items = value.map((item, index) => {
2193
+ const matched = matches[index];
2194
+ if (matched !== void 0)
2195
+ return oldItems[matched];
2196
+ const candidate = index < oldItems.length && !consumed.has(index) ? oldItems[index] : void 0;
2197
+ const sameItem = candidate !== void 0 && looksLikeSameItem(candidate, item) ? candidate : void 0;
2198
+ return reconcileNode(doc, sameItem, item);
2199
+ });
2200
+ }
2201
+ function applyDataToDocument(doc, data) {
2202
+ doc.contents = reconcileNode(doc, doc.contents, data);
2203
+ }
2204
+ function serializeYaml(data, existingRaw) {
2205
+ if (existingRaw === void 0)
2206
+ return yamlStringify(data);
2207
+ const doc = parseDocument(existingRaw);
2208
+ if (doc.errors.length > 0)
2209
+ return yamlStringify(data);
2210
+ applyDataToDocument(doc, data);
2211
+ return doc.toString();
2212
+ }
2213
+ function extractRawFrontmatter(raw) {
2214
+ let parsed;
2215
+ try {
2216
+ parsed = matter(raw, {});
2217
+ } catch {
2218
+ return void 0;
2219
+ }
2220
+ const frontmatter = parsed.matter;
2221
+ if (typeof frontmatter !== "string" || frontmatter.trim() === "")
2222
+ return void 0;
2223
+ return frontmatter;
2224
+ }
2225
+ function serializeFrontmatter(body, data, existingRaw) {
2226
+ if (existingRaw === void 0)
2227
+ return matter.stringify(body, data);
2228
+ const existingFrontmatter = extractRawFrontmatter(existingRaw);
2229
+ if (existingFrontmatter === void 0)
2230
+ return matter.stringify(body, data);
2231
+ const doc = parseDocument(existingFrontmatter);
2232
+ if (doc.errors.length > 0)
2233
+ return matter.stringify(body, data);
2234
+ applyDataToDocument(doc, data);
2235
+ const reconciled = doc.toString();
2236
+ return matter.stringify(body, data, {
2237
+ engines: {
2238
+ yaml: {
2239
+ parse: () => ({}),
2240
+ stringify: () => reconciled
2241
+ }
2242
+ }
2243
+ });
2244
+ }
2023
2245
  async function withLock(key, fn) {
2024
2246
  const prev = locks.get(key) ?? Promise.resolve();
2025
2247
  let resolve2;
@@ -2036,51 +2258,71 @@ async function withLock(key, fn) {
2036
2258
  locks.delete(key);
2037
2259
  }
2038
2260
  }
2039
- function getErrorMessage3(err) {
2040
- if (err instanceof Error) {
2041
- return err.message;
2042
- }
2043
- if (typeof err === "string") {
2044
- return err;
2045
- }
2046
- return String(err);
2047
- }
2048
- function isNodeError3(err) {
2049
- return err instanceof Error && "code" in err;
2050
- }
2051
- function isNotFoundError3(err) {
2052
- return isNodeError3(err) && err.code === "ENOENT";
2261
+ function provisioningLockOptions(lockPath, retries, onCompromised) {
2262
+ return {
2263
+ lockfilePath: lockPath,
2264
+ realpath: false,
2265
+ retries,
2266
+ stale: 3e4,
2267
+ // proper-lockfile invokes this from inside its refresh timer, so ANY throw
2268
+ // escaping here is an uncaught exception that kills the process -- the very
2269
+ // failure the handler exists to prevent. Call sites are told not to throw
2270
+ // (see OnLockCompromised); this makes it structural rather than a
2271
+ // convention, and also covers the LOGGER throwing: under `CI=true`,
2272
+ // vitest's `onConsoleLog` turns any console write into a throw.
2273
+ onCompromised: (err) => {
2274
+ try {
2275
+ if (onCompromised) {
2276
+ onCompromised(err);
2277
+ return;
2278
+ }
2279
+ canopyLogWarn2(`[canopy] Provisioning lock compromised mid-hold for ${lockPath}:`, getErrorMessage3(err));
2280
+ } catch {
2281
+ try {
2282
+ process.stderr.write(`[canopy] lock compromised for ${lockPath}; its handler or logger threw
2283
+ `);
2284
+ } catch {
2285
+ }
2286
+ }
2287
+ }
2288
+ };
2053
2289
  }
2054
- function isFileExistsError2(err) {
2055
- return isNodeError3(err) && err.code === "EEXIST";
2290
+ function releaseIgnoringAlreadyReleased(release, lockPath) {
2291
+ return async () => {
2292
+ try {
2293
+ await release();
2294
+ } catch (err) {
2295
+ if (isNodeError3(err) && err.code === "ERELEASED") {
2296
+ canopyLogWarn2(`[canopy] Lock at ${lockPath} was already released (compromised mid-hold, or released twice); nothing to release`);
2297
+ return;
2298
+ }
2299
+ throw err;
2300
+ }
2301
+ };
2056
2302
  }
2057
- async function acquireProvisioningLock(lockTargetDir2, lockName) {
2303
+ async function acquireProvisioningLock(lockTargetDir2, lockName, onCompromised) {
2058
2304
  await fs23.mkdir(lockTargetDir2, { recursive: true });
2059
- return lockfile.lock(lockTargetDir2, {
2060
- lockfilePath: path22.join(lockTargetDir2, lockName),
2061
- retries: { retries: 600, factor: 1, minTimeout: 300, maxTimeout: 800, randomize: true },
2062
- stale: 3e4
2063
- });
2305
+ const lockPath = path22.join(lockTargetDir2, lockName);
2306
+ const release = await lockfile.lock(lockPath, provisioningLockOptions(lockPath, { retries: 600, factor: 1, minTimeout: 300, maxTimeout: 800, randomize: true }, onCompromised));
2307
+ return releaseIgnoringAlreadyReleased(release, lockPath);
2064
2308
  }
2065
- async function tryAcquireProvisioningLock(lockTargetDir2, lockName) {
2309
+ async function tryAcquireProvisioningLock(lockTargetDir2, lockName, onCompromised) {
2066
2310
  await fs23.mkdir(lockTargetDir2, { recursive: true });
2067
- return lockfile.lock(lockTargetDir2, {
2068
- lockfilePath: path22.join(lockTargetDir2, lockName),
2069
- retries: 0,
2070
- stale: 3e4
2071
- });
2311
+ const lockPath = path22.join(lockTargetDir2, lockName);
2312
+ const release = await lockfile.lock(lockPath, provisioningLockOptions(lockPath, 0, onCompromised));
2313
+ return releaseIgnoringAlreadyReleased(release, lockPath);
2072
2314
  }
2073
2315
  function lockTargetDir(branchRoot) {
2074
2316
  return path32.join(path32.resolve(branchRoot), META_DIR);
2075
2317
  }
2076
- function tryAcquireContentWriteLock(branchRoot) {
2077
- return tryAcquireProvisioningLock(lockTargetDir(branchRoot), CONTENT_WRITE_LOCK_NAME);
2318
+ function tryAcquireContentWriteLock(branchRoot, onCompromised) {
2319
+ return tryAcquireProvisioningLock(lockTargetDir(branchRoot), CONTENT_WRITE_LOCK_NAME, onCompromised);
2078
2320
  }
2079
- async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS) {
2321
+ async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, onCompromised) {
2080
2322
  const startedAt = Date.now();
2081
2323
  for (let attempt = 0; ; attempt++) {
2082
2324
  try {
2083
- return await tryAcquireContentWriteLock(branchRoot);
2325
+ return await tryAcquireContentWriteLock(branchRoot, onCompromised);
2084
2326
  } catch (err) {
2085
2327
  if (!isNodeError3(err) || err.code !== "ELOCKED")
2086
2328
  throw err;
@@ -2093,12 +2335,21 @@ async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRIT
2093
2335
  }
2094
2336
  }
2095
2337
  async function withContentWriteLock(branchRoot, fn, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS) {
2096
- const release = await acquireContentWriteLock(branchRoot, waitMs);
2338
+ let compromised = false;
2339
+ const release = await acquireContentWriteLock(branchRoot, waitMs, (err) => {
2340
+ compromised = true;
2341
+ canopyLogWarn2(`[canopy] Content-write lock compromised mid-hold for ${branchRoot}:`, getErrorMessage3(err));
2342
+ });
2343
+ let result;
2097
2344
  try {
2098
- return await fn();
2345
+ result = await fn();
2099
2346
  } finally {
2100
2347
  await release();
2101
2348
  }
2349
+ if (compromised) {
2350
+ 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.");
2351
+ }
2352
+ return result;
2102
2353
  }
2103
2354
  function flattenGroupFields(fields) {
2104
2355
  const result = [];
@@ -2118,9 +2369,68 @@ function findBodyFieldName(fields) {
2118
2369
  }
2119
2370
  return "body";
2120
2371
  }
2372
+ function buildResolvedReference(data, meta, body) {
2373
+ const resolved = { ...data };
2374
+ if (body && body.value)
2375
+ resolved[body.fieldName] = body.value;
2376
+ resolved.id = meta.id;
2377
+ resolved.slug = meta.slug;
2378
+ resolved.collection = meta.collection;
2379
+ resolved.urlPath = meta.urlPath;
2380
+ return resolved;
2381
+ }
2382
+ function isIndexSlug(slug) {
2383
+ return slug?.toLowerCase() === "index";
2384
+ }
2385
+ function computeEntryUrl(collection, slug, contentRoot) {
2386
+ const root = trimSlashes(contentRoot);
2387
+ let stripped = collection;
2388
+ if (root && collection.startsWith(`${root}/`)) {
2389
+ stripped = collection.slice(root.length + 1);
2390
+ } else if (collection === root) {
2391
+ stripped = "";
2392
+ }
2393
+ const segments = stripped.split("/").filter(Boolean);
2394
+ if (slug && !isIndexSlug(slug)) {
2395
+ segments.push(slug);
2396
+ }
2397
+ const path212 = segments.length > 0 ? `/${segments.join("/")}` : "/";
2398
+ return path212.toLowerCase();
2399
+ }
2121
2400
  function isValidContentId(id) {
2122
2401
  return CONTENT_ID_PATTERN.test(id);
2123
2402
  }
2403
+ function parseSlug(slug) {
2404
+ if (!slug) {
2405
+ return {
2406
+ ok: false,
2407
+ error: "Slug is required"
2408
+ };
2409
+ }
2410
+ if (slug.length > 64) {
2411
+ return { ok: false, error: "Slug too long (max 64 characters)" };
2412
+ }
2413
+ if (slug.includes("/") || slug.includes("\\")) {
2414
+ return {
2415
+ ok: false,
2416
+ error: "Slug cannot contain path separators"
2417
+ };
2418
+ }
2419
+ if (slug === "." || slug === "..") {
2420
+ return {
2421
+ ok: false,
2422
+ error: "Slug cannot be a traversal sequence"
2423
+ };
2424
+ }
2425
+ const normalized = slug.toLowerCase();
2426
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(normalized)) {
2427
+ return {
2428
+ ok: false,
2429
+ error: "Slug must start with a letter or number and contain only lowercase letters, numbers, and hyphens"
2430
+ };
2431
+ }
2432
+ return { ok: true, slug: normalized };
2433
+ }
2124
2434
  function generateId() {
2125
2435
  const full = generate();
2126
2436
  return full.substring(0, 12);
@@ -2148,13 +2458,13 @@ function extractIdFromFilename(filename) {
2148
2458
  return null;
2149
2459
  }
2150
2460
  async function resolveCollectionPath(root, logicalPath) {
2151
- const fs172 = await import("node:fs/promises");
2152
- const path202 = await import("node:path");
2461
+ const fs182 = await import("node:fs/promises");
2462
+ const path212 = await import("node:path");
2153
2463
  const segments = logicalPath.split("/").filter(Boolean);
2154
2464
  let currentPath = root;
2155
2465
  for (const segment of segments) {
2156
2466
  try {
2157
- const entries = await fs172.readdir(currentPath, { withFileTypes: true });
2467
+ const entries = await fs182.readdir(currentPath, { withFileTypes: true });
2158
2468
  const matchingDir = entries.find((entry) => {
2159
2469
  if (!entry.isDirectory())
2160
2470
  return false;
@@ -2162,7 +2472,7 @@ async function resolveCollectionPath(root, logicalPath) {
2162
2472
  return logicalName === segment.toLowerCase();
2163
2473
  });
2164
2474
  if (matchingDir) {
2165
- currentPath = path202.join(currentPath, matchingDir.name);
2475
+ currentPath = path212.join(currentPath, matchingDir.name);
2166
2476
  } else {
2167
2477
  return null;
2168
2478
  }
@@ -2211,8 +2521,70 @@ function extractSlugFromFilename(filename, entryTypeName) {
2211
2521
  }
2212
2522
  return filename.toLowerCase();
2213
2523
  }
2524
+ async function readDirSafe(dir) {
2525
+ try {
2526
+ return await fs42.readdir(dir, { withFileTypes: true });
2527
+ } catch (err) {
2528
+ if (isNotFoundError3(err))
2529
+ return [];
2530
+ throw err;
2531
+ }
2532
+ }
2533
+ async function findChildCollectionDir(dir, name) {
2534
+ const wanted = name.toLowerCase();
2535
+ for (const entry of await readDirSafe(dir)) {
2536
+ if (!entry.isDirectory())
2537
+ continue;
2538
+ if (extractSlugFromFilename(entry.name) === wanted)
2539
+ return path52.join(dir, entry.name);
2540
+ }
2541
+ return null;
2542
+ }
2543
+ function entrySlugOf(filename) {
2544
+ if (!CONTENT_EXTENSIONS.some((ext) => filename.endsWith(ext)))
2545
+ return null;
2546
+ return parseTypedFilename(filename)?.slug ?? null;
2547
+ }
2548
+ async function findIndexEntryIn(dir) {
2549
+ for (const entry of await readDirSafe(dir)) {
2550
+ if (entry.isDirectory())
2551
+ continue;
2552
+ if (isIndexSlug(entrySlugOf(entry.name) ?? void 0))
2553
+ return path52.join(dir, entry.name);
2554
+ }
2555
+ return null;
2556
+ }
2557
+ async function findEntryBySlugIn(dir, slug) {
2558
+ const wanted = slug.toLowerCase();
2559
+ for (const entry of await readDirSafe(dir)) {
2560
+ if (entry.isDirectory())
2561
+ continue;
2562
+ if (entrySlugOf(entry.name) === wanted)
2563
+ return path52.join(dir, entry.name);
2564
+ }
2565
+ return null;
2566
+ }
2567
+ async function findUrlPathClaimant(opts) {
2568
+ const { collectionDir, slug, contentRoot } = opts;
2569
+ if (isIndexSlug(slug)) {
2570
+ const resolvedRoot = path52.resolve(contentRoot);
2571
+ if (path52.resolve(collectionDir) === resolvedRoot)
2572
+ return null;
2573
+ const parentDir = path52.dirname(collectionDir);
2574
+ const ownName = extractSlugFromFilename(path52.basename(collectionDir));
2575
+ if (isIndexSlug(ownName))
2576
+ return null;
2577
+ const claimant = await findEntryBySlugIn(parentDir, ownName);
2578
+ return claimant ? { kind: "parent-entry", physicalPath: claimant, name: ownName } : null;
2579
+ }
2580
+ const childDir = await findChildCollectionDir(collectionDir, slug);
2581
+ if (!childDir)
2582
+ return null;
2583
+ const indexEntry = await findIndexEntryIn(childDir);
2584
+ return indexEntry ? { kind: "sibling-collection-index", physicalPath: indexEntry, name: slug } : null;
2585
+ }
2214
2586
  function registerContentIndexForInvalidation(root, target) {
2215
- const rootKey = path52.resolve(root);
2587
+ const rootKey = path62.resolve(root);
2216
2588
  let refs = registry.get(rootKey);
2217
2589
  if (!refs) {
2218
2590
  refs = /* @__PURE__ */ new Set();
@@ -2223,8 +2595,8 @@ function registerContentIndexForInvalidation(root, target) {
2223
2595
  finalization.register(target, { rootKey, ref });
2224
2596
  }
2225
2597
  function invalidateContentIndexesForRoot(root) {
2226
- const rootKey = path52.resolve(root);
2227
- const prefix = rootKey + path52.sep;
2598
+ const rootKey = path62.resolve(root);
2599
+ const prefix = rootKey + path62.sep;
2228
2600
  for (const [storeRoot, refs] of registry) {
2229
2601
  if (storeRoot !== rootKey && !storeRoot.startsWith(prefix))
2230
2602
  continue;
@@ -2244,7 +2616,7 @@ function createDebugLogger2(options) {
2244
2616
  return new DebugLogger2(options);
2245
2617
  }
2246
2618
  function resourceGenerationPath(root, resource) {
2247
- return path62.resolve(root, META_DIR2, `${resource}.generation`);
2619
+ return path72.resolve(root, META_DIR2, `${resource}.generation`);
2248
2620
  }
2249
2621
  async function bumpResourceGeneration(root, resource, options) {
2250
2622
  const token = randomUUID();
@@ -2262,7 +2634,7 @@ async function bumpResourceGeneration(root, resource, options) {
2262
2634
  }
2263
2635
  async function readResourceGeneration(root, resource) {
2264
2636
  try {
2265
- const token = await fs42.readFile(resourceGenerationPath(root, resource), "utf-8");
2637
+ const token = await fs52.readFile(resourceGenerationPath(root, resource), "utf-8");
2266
2638
  return { ok: true, token };
2267
2639
  } catch (err) {
2268
2640
  if (isNodeError3(err) && err.code === "ENOENT")
@@ -2282,7 +2654,7 @@ function stripEmbeddedIdFromName(name) {
2282
2654
  async function scanForCollectionMeta(baseDir, relativePath = "") {
2283
2655
  const collections = [];
2284
2656
  try {
2285
- const entries = await fs52.readdir(baseDir, { withFileTypes: true });
2657
+ const entries = await fs62.readdir(baseDir, { withFileTypes: true });
2286
2658
  for (const entry of entries) {
2287
2659
  if (!entry.isDirectory())
2288
2660
  continue;
@@ -2293,8 +2665,8 @@ async function scanForCollectionMeta(baseDir, relativePath = "") {
2293
2665
  const absolutePath = join3(baseDir, folderName);
2294
2666
  const metaPath = join3(absolutePath, ".collection.json");
2295
2667
  try {
2296
- await fs52.access(metaPath);
2297
- const content = await fs52.readFile(metaPath, "utf-8");
2668
+ await fs62.access(metaPath);
2669
+ const content = await fs62.readFile(metaPath, "utf-8");
2298
2670
  const parsed = JSON.parse(content);
2299
2671
  const meta = collectionMetaSchema.parse(parsed);
2300
2672
  collections.push({
@@ -2326,7 +2698,7 @@ async function loadCollectionMetaFiles(contentRoot) {
2326
2698
  let root = null;
2327
2699
  const rootMetaPath = join3(contentRoot, ".collection.json");
2328
2700
  try {
2329
- await fs52.access(rootMetaPath);
2701
+ await fs62.access(rootMetaPath);
2330
2702
  } catch (err) {
2331
2703
  if (err.code === "ENOENT") {
2332
2704
  } else {
@@ -2334,7 +2706,7 @@ async function loadCollectionMetaFiles(contentRoot) {
2334
2706
  }
2335
2707
  }
2336
2708
  try {
2337
- const content = await fs52.readFile(rootMetaPath, "utf-8");
2709
+ const content = await fs62.readFile(rootMetaPath, "utf-8");
2338
2710
  const parsed = JSON.parse(content);
2339
2711
  root = rootCollectionMetaSchema.parse(parsed);
2340
2712
  } catch (err) {
@@ -2424,16 +2796,16 @@ function isValidSchema(schema) {
2424
2796
  async function isStaleByMtime(dir, cachedAt) {
2425
2797
  let entries;
2426
2798
  try {
2427
- entries = await fs62.readdir(dir, { recursive: true, encoding: "utf-8" });
2799
+ entries = await fs72.readdir(dir, { recursive: true, encoding: "utf-8" });
2428
2800
  } catch {
2429
2801
  return true;
2430
2802
  }
2431
2803
  for (const entry of entries) {
2432
2804
  if (!entry.endsWith(".collection.json"))
2433
2805
  continue;
2434
- const full = path72.join(dir, entry);
2806
+ const full = path82.join(dir, entry);
2435
2807
  try {
2436
- const stat = await fs62.stat(full);
2808
+ const stat = await fs72.stat(full);
2437
2809
  if (stat.mtimeMs > cachedAt.getTime())
2438
2810
  return true;
2439
2811
  } catch {
@@ -2456,7 +2828,7 @@ async function invalidateBranchContentCaches(root) {
2456
2828
  }
2457
2829
  async function filePathExists3(filePath) {
2458
2830
  try {
2459
- await fs72.stat(filePath);
2831
+ await fs8.stat(filePath);
2460
2832
  return true;
2461
2833
  } catch (err) {
2462
2834
  if (isNotFoundError3(err))
@@ -2464,6 +2836,15 @@ async function filePathExists3(filePath) {
2464
2836
  throw err;
2465
2837
  }
2466
2838
  }
2839
+ async function readFileIfExists(filePath) {
2840
+ try {
2841
+ return await fs8.readFile(filePath, "utf8");
2842
+ } catch (err) {
2843
+ if (isNotFoundError3(err))
2844
+ return void 0;
2845
+ throw err;
2846
+ }
2847
+ }
2467
2848
  function validateAndNormalizePath(root, target) {
2468
2849
  const resolvedRoot = resolve(root);
2469
2850
  const withSep = resolvedRoot.endsWith(sep) ? resolvedRoot : `${resolvedRoot}${sep}`;
@@ -2493,11 +2874,11 @@ function resolveBranchPath(options) {
2493
2874
  const safeBranch = sanitizeBranchName(options.branchName);
2494
2875
  const strategy = operatingStrategy2(options.mode);
2495
2876
  const baseRoot = resolveContentBranchesRoot(options.mode, options.basePathOverride);
2496
- const normalizedBase = path92.resolve(baseRoot);
2497
- const baseWithSep = normalizedBase.endsWith(path92.sep) ? normalizedBase : `${normalizedBase}${path92.sep}`;
2877
+ const normalizedBase = path102.resolve(baseRoot);
2878
+ const baseWithSep = normalizedBase.endsWith(path102.sep) ? normalizedBase : `${normalizedBase}${path102.sep}`;
2498
2879
  const branchRoot = strategy.getContentBranchRoot(safeBranch, options.basePathOverride);
2499
2880
  const withinBase = (target) => {
2500
- const resolved = path92.resolve(target);
2881
+ const resolved = path102.resolve(target);
2501
2882
  return resolved === normalizedBase || resolved.startsWith(baseWithSep);
2502
2883
  };
2503
2884
  if (!withinBase(branchRoot)) {
@@ -2507,7 +2888,7 @@ function resolveBranchPath(options) {
2507
2888
  }
2508
2889
  async function ensureBranchRoot(options) {
2509
2890
  const result = resolveBranchPath(options);
2510
- await fs8.mkdir(result.branchRoot, { recursive: true });
2891
+ await fs9.mkdir(result.branchRoot, { recursive: true });
2511
2892
  return result;
2512
2893
  }
2513
2894
  async function withLocks(keys, fn) {
@@ -2590,14 +2971,44 @@ function parseComponentProps(attrString) {
2590
2971
  }
2591
2972
  return props;
2592
2973
  }
2974
+ function isFenceCloseLine(line, marker) {
2975
+ return line.startsWith(marker) && /^\s*$/.test(line.slice(marker.length));
2976
+ }
2977
+ function maskFencedCodeBlocks(body, blocks) {
2978
+ const lines = body.split("\n");
2979
+ const lineCount = lines.length;
2980
+ const nextCloseLine = {
2981
+ "```": new Array(lineCount + 1).fill(-1),
2982
+ "~~~": new Array(lineCount + 1).fill(-1)
2983
+ };
2984
+ for (let i2 = lineCount - 1; i2 >= 0; i2--) {
2985
+ nextCloseLine["```"][i2] = isFenceCloseLine(lines[i2], "```") ? i2 : nextCloseLine["```"][i2 + 1];
2986
+ nextCloseLine["~~~"][i2] = isFenceCloseLine(lines[i2], "~~~") ? i2 : nextCloseLine["~~~"][i2 + 1];
2987
+ }
2988
+ const outLines = [];
2989
+ let i = 0;
2990
+ while (i < lineCount) {
2991
+ const line = lines[i];
2992
+ const marker = line.startsWith("```") ? "```" : line.startsWith("~~~") ? "~~~" : null;
2993
+ const searchFrom = i + 2;
2994
+ const closeIndex = marker && searchFrom < lineCount ? nextCloseLine[marker][searchFrom] : -1;
2995
+ if (!marker || closeIndex === -1) {
2996
+ outLines.push(line);
2997
+ i++;
2998
+ continue;
2999
+ }
3000
+ const block = lines.slice(i, closeIndex + 1).join("\n");
3001
+ const idx = blocks.length;
3002
+ blocks.push(block);
3003
+ outLines.push(`${BLOCK_PREFIX}${idx}${BLOCK_SUFFIX}`);
3004
+ i = closeIndex + 1;
3005
+ }
3006
+ return outLines.join("\n");
3007
+ }
2593
3008
  function maskCodeBlocks(body) {
2594
3009
  const blocks = [];
2595
3010
  const inlines = [];
2596
- let masked = body.replace(/^(```|~~~).*\n[\s\S]*?\n\1\s*$/gm, (block) => {
2597
- const idx = blocks.length;
2598
- blocks.push(block);
2599
- return `${BLOCK_PREFIX}${idx}${BLOCK_SUFFIX}`;
2600
- });
3011
+ let masked = maskFencedCodeBlocks(body, blocks);
2601
3012
  masked = masked.replace(/``[^`]+``|`[^`]+`/g, (span) => {
2602
3013
  const idx = inlines.length;
2603
3014
  inlines.push(span);
@@ -2765,7 +3176,7 @@ function renderField(field, value, depth, entry, config) {
2765
3176
  const heading = "#".repeat(Math.min(depth, 6));
2766
3177
  const descriptionLine = "description" in field && field.description ? `
2767
3178
 
2768
- *${field.description}*` : "";
3179
+ _${field.description}_` : "";
2769
3180
  if ("list" in field && field.list && Array.isArray(value)) {
2770
3181
  return renderListField(field, value, depth, label, heading, descriptionLine, entry, config);
2771
3182
  }
@@ -3021,26 +3432,13 @@ function formatInlineValue(field, value) {
3021
3432
  return String(value);
3022
3433
  }
3023
3434
  function yamlValue(value) {
3435
+ if (value === "")
3436
+ return '""';
3024
3437
  if (/[:#{}[\],&*?|>!%@`]/.test(value) || value.includes("\n")) {
3025
3438
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
3026
3439
  }
3027
3440
  return value;
3028
3441
  }
3029
- function computeEntryUrl(collection, slug, contentRoot) {
3030
- const root = trimSlashes(contentRoot);
3031
- let stripped = collection;
3032
- if (root && collection.startsWith(`${root}/`)) {
3033
- stripped = collection.slice(root.length + 1);
3034
- } else if (collection === root) {
3035
- stripped = "";
3036
- }
3037
- const segments = stripped.split("/").filter(Boolean);
3038
- if (slug && slug !== "index") {
3039
- segments.push(slug);
3040
- }
3041
- const path202 = segments.length > 0 ? `/${segments.join("/")}` : "/";
3042
- return path202.toLowerCase();
3043
- }
3044
3442
  function resolveEntryUrl(location, contentRoot) {
3045
3443
  return computeEntryUrl(location.collection ?? "", location.slug ?? "", contentRoot);
3046
3444
  }
@@ -3207,7 +3605,7 @@ async function processCollection(store, collection, flatSchema, contentRoot, con
3207
3605
  const listed = await store.getCollectionEntryPaths(collection.logicalPath);
3208
3606
  const directEntries = listed.filter((e) => e.collection === collection.logicalPath);
3209
3607
  for (const listEntry of directEntries) {
3210
- const entryTypeName = extractEntryTypeFromFilename(path11.basename(listEntry.relativePath));
3608
+ const entryTypeName = extractEntryTypeFromFilename(path12.basename(listEntry.relativePath));
3211
3609
  if (!entryTypeName)
3212
3610
  continue;
3213
3611
  if (config?.exclude?.entryTypes?.includes(entryTypeName))
@@ -3225,7 +3623,7 @@ async function processCollection(store, collection, flatSchema, contentRoot, con
3225
3623
  }
3226
3624
  if (config?.exclude?.where?.(aiEntry))
3227
3625
  continue;
3228
- await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path11.basename(listEntry.relativePath)), config);
3626
+ await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path12.basename(listEntry.relativePath)), config);
3229
3627
  entries.push(aiEntry);
3230
3628
  const entryFilePath = `${cleanPath}/${listEntry.slug}.md`;
3231
3629
  const entryMarkdown = entryToMarkdown(aiEntry, config);
@@ -3276,7 +3674,7 @@ async function processRootEntries(store, rootCollection, contentRoot, config, id
3276
3674
  const listed = await store.getCollectionEntryPaths(rootCollection.logicalPath);
3277
3675
  const directEntries = listed.filter((e) => e.collection === rootCollection.logicalPath);
3278
3676
  for (const listEntry of directEntries) {
3279
- const entryTypeName = extractEntryTypeFromFilename(path11.basename(listEntry.relativePath));
3677
+ const entryTypeName = extractEntryTypeFromFilename(path12.basename(listEntry.relativePath));
3280
3678
  if (!entryTypeName)
3281
3679
  continue;
3282
3680
  if (config?.exclude?.entryTypes?.includes(entryTypeName))
@@ -3294,7 +3692,7 @@ async function processRootEntries(store, rootCollection, contentRoot, config, id
3294
3692
  }
3295
3693
  if (config?.exclude?.where?.(aiEntry))
3296
3694
  continue;
3297
- await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path11.basename(listEntry.relativePath)), config);
3695
+ await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path12.basename(listEntry.relativePath)), config);
3298
3696
  entries.push(aiEntry);
3299
3697
  const entryFilePath = `${listEntry.slug}.md`;
3300
3698
  const entryMarkdown = entryToMarkdown(aiEntry, config);
@@ -3342,19 +3740,19 @@ function docToAIEntry(doc, slug, entryTypeName, entryTypeConfig, cleanCollection
3342
3740
  };
3343
3741
  }
3344
3742
  function makeReadSibling(dir) {
3345
- const resolvedDir = path11.resolve(dir);
3743
+ const resolvedDir = path12.resolve(dir);
3346
3744
  return async (name) => {
3347
- if (!name || name.includes("/") || name.includes("\\") || path11.isAbsolute(name) || hasTraversalSequence(name)) {
3745
+ if (!name || name.includes("/") || name.includes("\\") || path12.isAbsolute(name) || hasTraversalSequence(name)) {
3348
3746
  return null;
3349
3747
  }
3350
- const abs = path11.resolve(resolvedDir, name);
3351
- if (path11.dirname(abs) !== resolvedDir)
3748
+ const abs = path12.resolve(resolvedDir, name);
3749
+ if (path12.dirname(abs) !== resolvedDir)
3352
3750
  return null;
3353
3751
  try {
3354
- const stats = await fs10.lstat(abs);
3752
+ const stats = await fs11.lstat(abs);
3355
3753
  if (!stats.isFile())
3356
3754
  return null;
3357
- return await fs10.readFile(abs, "utf8");
3755
+ return await fs11.readFile(abs, "utf8");
3358
3756
  } catch (err) {
3359
3757
  if (isNodeError3(err) && (err.code === "ENOENT" || err.code === "ENOTDIR"))
3360
3758
  return null;
@@ -3368,7 +3766,7 @@ async function runEntryTransform(entry, absolutePath, contentId, config) {
3368
3766
  return;
3369
3767
  const ctx = {
3370
3768
  contentId: contentId ?? "",
3371
- readSibling: makeReadSibling(path11.dirname(absolutePath))
3769
+ readSibling: makeReadSibling(path12.dirname(absolutePath))
3372
3770
  };
3373
3771
  try {
3374
3772
  const appended = await transform(entry, ctx);
@@ -3409,29 +3807,29 @@ async function writeOccJsonFile(filePath, payload, options) {
3409
3807
  const writeId = randomUUID2();
3410
3808
  const data = { ...payload, version: newVersion, writeId };
3411
3809
  const content = JSON.stringify(data, null, 2) + (trailingNewline ? "\n" : "");
3412
- const dir = path13.dirname(filePath);
3413
- await fs12.mkdir(dir, { recursive: true });
3810
+ const dir = path14.dirname(filePath);
3811
+ await fs13.mkdir(dir, { recursive: true });
3414
3812
  const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
3415
- await fs12.writeFile(tempPath, content, "utf-8");
3813
+ await fs13.writeFile(tempPath, content, "utf-8");
3416
3814
  if (expectedVersion === null) {
3417
3815
  try {
3418
- await fs12.link(tempPath, filePath);
3816
+ await fs13.link(tempPath, filePath);
3419
3817
  } catch (err) {
3420
- await fs12.unlink(tempPath).catch(() => {
3818
+ await fs13.unlink(tempPath).catch(() => {
3421
3819
  });
3422
3820
  if (isFileExistsError2(err)) {
3423
3821
  throw new OccWriteConflictError();
3424
3822
  }
3425
3823
  throw err;
3426
3824
  }
3427
- await fs12.unlink(tempPath).catch(() => {
3825
+ await fs13.unlink(tempPath).catch(() => {
3428
3826
  });
3429
3827
  return { version: newVersion, writeId };
3430
3828
  }
3431
3829
  try {
3432
3830
  let currentVersion = null;
3433
3831
  try {
3434
- const current = JSON.parse(await fs12.readFile(filePath, "utf-8"));
3832
+ const current = JSON.parse(await fs13.readFile(filePath, "utf-8"));
3435
3833
  currentVersion = current.version ?? 0;
3436
3834
  } catch {
3437
3835
  currentVersion = null;
@@ -3439,16 +3837,16 @@ async function writeOccJsonFile(filePath, payload, options) {
3439
3837
  if (currentVersion !== expectedVersion) {
3440
3838
  throw new OccWriteConflictError();
3441
3839
  }
3442
- await fs12.rename(tempPath, filePath);
3840
+ await fs13.rename(tempPath, filePath);
3443
3841
  if (settleMs > 0) {
3444
3842
  await new Promise((resolve2) => setTimeout(resolve2, settleMs));
3445
3843
  }
3446
- const afterWrite = JSON.parse(await fs12.readFile(filePath, "utf-8"));
3844
+ const afterWrite = JSON.parse(await fs13.readFile(filePath, "utf-8"));
3447
3845
  if (afterWrite.writeId !== writeId) {
3448
3846
  throw new OccWriteConflictError();
3449
3847
  }
3450
3848
  } catch (err) {
3451
- await fs12.unlink(tempPath).catch(() => {
3849
+ await fs13.unlink(tempPath).catch(() => {
3452
3850
  });
3453
3851
  throw err;
3454
3852
  }
@@ -3474,8 +3872,8 @@ async function withOccRetry(operation, options) {
3474
3872
  throw new Error("Unreachable");
3475
3873
  }
3476
3874
  async function withOccFileLock(filePath, fn) {
3477
- const dir = path13.dirname(filePath);
3478
- await fs12.mkdir(dir, { recursive: true });
3875
+ const dir = path14.dirname(filePath);
3876
+ await fs13.mkdir(dir, { recursive: true });
3479
3877
  const MAX_ATTEMPTS = 14;
3480
3878
  let release = null;
3481
3879
  for (let attempt = 1; release === null; attempt++) {
@@ -3559,11 +3957,11 @@ function gitChildEnv(overrides) {
3559
3957
  return { ...env, ...FORCE_C_LOCALE, ...overrides };
3560
3958
  }
3561
3959
  async function ensureGitExcludePattern(repoPath, pattern) {
3562
- const excludePath = path15.join(repoPath, ".git", "info", "exclude");
3563
- await fs14.mkdir(path15.dirname(excludePath), { recursive: true });
3960
+ const excludePath = path16.join(repoPath, ".git", "info", "exclude");
3961
+ await fs15.mkdir(path16.dirname(excludePath), { recursive: true });
3564
3962
  let content = "";
3565
3963
  try {
3566
- content = await fs14.readFile(excludePath, "utf-8");
3964
+ content = await fs15.readFile(excludePath, "utf-8");
3567
3965
  } catch (err) {
3568
3966
  if (!isNotFoundError3(err))
3569
3967
  throw err;
@@ -3575,7 +3973,7 @@ async function ensureGitExcludePattern(repoPath, pattern) {
3575
3973
  }
3576
3974
  const needsLeadingNewline = content.length > 0 && !content.endsWith("\n");
3577
3975
  const newContent = content + (needsLeadingNewline ? "\n" : "") + pattern + "\n";
3578
- await fs14.writeFile(excludePath, newContent, "utf-8");
3976
+ await fs15.writeFile(excludePath, newContent, "utf-8");
3579
3977
  log5.debug("git", "Added pattern to .git/info/exclude", { pattern });
3580
3978
  }
3581
3979
  async function loadOrCreateBranchContext(options) {
@@ -3631,24 +4029,34 @@ async function resolveBranchRoot(config) {
3631
4029
  });
3632
4030
  return context.branchRoot;
3633
4031
  }
3634
- async function listEntries(branchRoot, flatSchema, contentRootName, options) {
4032
+ async function listEntries(branchRoot, flatSchema, contentRootName, options, visibility) {
3635
4033
  const rootPath = options?.rootPath ?? contentRootName;
3636
4034
  const extract = options?.extract;
3637
4035
  const filter = options?.filter;
3638
4036
  const customSort = options?.sort;
3639
4037
  const collections = flatSchema.filter((item) => item.type === "collection" && item.entries !== void 0 && (item.logicalPath === rootPath || item.logicalPath.startsWith(`${rootPath}/`)));
4038
+ const shouldInclude = visibility?.shouldInclude;
4039
+ const skippedFiles = [];
4040
+ const resolver = options?.resolveReferences ? createReferenceResolver(branchRoot, flatSchema, contentRootName) : null;
3640
4041
  const collectionResults = await Promise.all(collections.map(async (collection) => {
3641
- const entries = await listCollectionEntries(branchRoot, collection);
3642
- return entries.map((entry) => ({ entry, collection }));
4042
+ const entries = await listCollectionEntries(branchRoot, collection, (file) => skippedFiles.push(file));
4043
+ const visible = shouldInclude ? entries.filter((e) => shouldInclude(e.physicalPath)) : entries;
4044
+ const resolved = resolver ? await resolveCollectionItemReferences(visible, collection, resolver) : visible;
4045
+ return resolved.map((entry) => ({ entry, collection }));
3643
4046
  }));
4047
+ if (isBuildMode() && skippedFiles.length > 0) {
4048
+ const lines = skippedFiles.map(({ filename, collectionPath }) => ` - ${collectionPath}/${filename}`);
4049
+ throw new Error(`CanopyCMS static build: found ${skippedFiles.length} file(s) that look like malformed content entries inside a content collection:
4050
+ ${lines.join("\n")}
4051
+ Expected {type}.{slug}.{id}.{ext} with a known entry type (a name declared in that collection's entries config) and a valid 12-char Base58 ID. This usually means a schema rename without a matching file rename, an entry type declared in one collection but not another, or a stray file placed directly in a collection directory. If this is meant to be a colocated sibling artifact (e.g. an entry's contentId-prefixed data file read via an entryTransforms readSibling call), keep its name to three or fewer dot-separated segments (id.suffix.ext) so it is never mistaken for an entry attempt. Otherwise rename or move the file, or fix the schema, then rebuild.`);
4052
+ }
3644
4053
  const contentPrefix = contentRootName ? `${contentRootName}/` : "";
3645
4054
  const items = [];
3646
4055
  for (const results of collectionResults) {
3647
4056
  for (const { entry, collection } of results) {
3648
4057
  const pathWithoutRoot = entry.logicalPath.startsWith(contentPrefix) ? entry.logicalPath.slice(contentPrefix.length) : entry.logicalPath;
3649
4058
  const pathSegments = pathWithoutRoot.split("/").filter(Boolean);
3650
- const urlSegments = entry.slug === "index" ? pathSegments.slice(0, -1) : pathSegments;
3651
- const urlPath = urlSegments.length > 0 ? `/${urlSegments.join("/")}`.toLowerCase() : "/";
4059
+ const urlPath = computeEntryUrl(entry.collectionPath, entry.slug, contentRootName);
3652
4060
  const raw = entry.data;
3653
4061
  const meta = {
3654
4062
  entryPath: entry.logicalPath,
@@ -3667,7 +4075,8 @@ async function listEntries(branchRoot, flatSchema, contentRootName, options) {
3667
4075
  entryType: entry.entryType,
3668
4076
  format: entry.format,
3669
4077
  data,
3670
- schema: collection.entries?.find((e) => e.name === entry.entryType)?.schema
4078
+ schema: collection.entries?.find((e) => e.name === entry.entryType)?.schema,
4079
+ updatedAt: entry.updatedAt
3671
4080
  };
3672
4081
  if (filter && !filter(item))
3673
4082
  continue;
@@ -3692,7 +4101,7 @@ function isValidCropRect(x, y, w, h) {
3692
4101
  return false;
3693
4102
  return true;
3694
4103
  }
3695
- function isPlainRecord(value) {
4104
+ function isPlainRecord2(value) {
3696
4105
  return typeof value === "object" && value !== null && !Array.isArray(value);
3697
4106
  }
3698
4107
  function resolveBlockItem(blockField, item) {
@@ -3702,17 +4111,17 @@ function resolveBlockItem(blockField, item) {
3702
4111
  const template = blockField.templates?.find((t) => t.name === templateName);
3703
4112
  if (!template?.fields)
3704
4113
  return void 0;
3705
- const data = isPlainRecord(item.value) ? item.value : item;
4114
+ const data = isPlainRecord2(item.value) ? item.value : item;
3706
4115
  return { fields: template.fields, data };
3707
4116
  }
3708
- function isPlainRecord2(value) {
4117
+ function isPlainRecord3(value) {
3709
4118
  return typeof value === "object" && value !== null && !Array.isArray(value);
3710
4119
  }
3711
4120
  function isPositiveInt(value) {
3712
4121
  return typeof value === "number" && Number.isInteger(value) && value > 0;
3713
4122
  }
3714
4123
  function isValidImageCropValue(value) {
3715
- if (!isPlainRecord2(value))
4124
+ if (!isPlainRecord3(value))
3716
4125
  return false;
3717
4126
  const { x, y, w, h } = value;
3718
4127
  if (typeof x !== "number" || typeof y !== "number" || typeof w !== "number" || typeof h !== "number") {
@@ -3720,78 +4129,78 @@ function isValidImageCropValue(value) {
3720
4129
  }
3721
4130
  return isValidCropRect(x, y, w, h);
3722
4131
  }
3723
- function validateImageValue(field, value, path202) {
3724
- if (!isPlainRecord2(value)) {
3725
- return [{ fieldPath: path202, message: "Expected an image object with { src, alt }" }];
4132
+ function validateImageValue(field, value, path212) {
4133
+ if (!isPlainRecord3(value)) {
4134
+ return [{ fieldPath: path212, message: "Expected an image object with { src, alt }" }];
3726
4135
  }
3727
4136
  const errors = [];
3728
4137
  if (typeof value.src !== "string" || value.src.trim() === "") {
3729
- errors.push({ fieldPath: `${path202}.src`, message: "Image src is required" });
4138
+ errors.push({ fieldPath: `${path212}.src`, message: "Image src is required" });
3730
4139
  }
3731
4140
  if (typeof value.alt !== "string") {
3732
- errors.push({ fieldPath: `${path202}.alt`, message: "Image alt text is required" });
4141
+ errors.push({ fieldPath: `${path212}.alt`, message: "Image alt text is required" });
3733
4142
  } else if (value.alt.trim() === "" && field.altOptional !== true) {
3734
- errors.push({ fieldPath: `${path202}.alt`, message: "Image alt text is required" });
4143
+ errors.push({ fieldPath: `${path212}.alt`, message: "Image alt text is required" });
3735
4144
  }
3736
4145
  if (value.width !== void 0 && !isPositiveInt(value.width)) {
3737
- errors.push({ fieldPath: `${path202}.width`, message: "Image width must be a positive integer" });
4146
+ errors.push({ fieldPath: `${path212}.width`, message: "Image width must be a positive integer" });
3738
4147
  }
3739
4148
  if (value.height !== void 0 && !isPositiveInt(value.height)) {
3740
4149
  errors.push({
3741
- fieldPath: `${path202}.height`,
4150
+ fieldPath: `${path212}.height`,
3742
4151
  message: "Image height must be a positive integer"
3743
4152
  });
3744
4153
  }
3745
4154
  if (value.crop !== void 0 && !isValidImageCropValue(value.crop)) {
3746
- errors.push({ fieldPath: `${path202}.crop`, message: "Invalid image crop rect" });
4155
+ errors.push({ fieldPath: `${path212}.crop`, message: "Invalid image crop rect" });
3747
4156
  }
3748
4157
  return errors;
3749
4158
  }
3750
4159
  function referenceValueId(value) {
3751
4160
  if (typeof value === "string")
3752
4161
  return value;
3753
- if (isPlainRecord2(value) && typeof value.id === "string")
4162
+ if (isPlainRecord3(value) && typeof value.id === "string")
3754
4163
  return value.id;
3755
4164
  return void 0;
3756
4165
  }
3757
- function validateScalar(field, value, path202) {
4166
+ function validateScalar(field, value, path212) {
3758
4167
  switch (field.type) {
3759
4168
  case "number":
3760
4169
  if (typeof value !== "number" || !Number.isFinite(value)) {
3761
- return [{ fieldPath: path202, message: "Expected a number" }];
4170
+ return [{ fieldPath: path212, message: "Expected a number" }];
3762
4171
  }
3763
4172
  return [];
3764
4173
  case "boolean":
3765
4174
  if (typeof value !== "boolean") {
3766
- return [{ fieldPath: path202, message: "Expected true or false" }];
4175
+ return [{ fieldPath: path212, message: "Expected true or false" }];
3767
4176
  }
3768
4177
  return [];
3769
4178
  case "datetime":
3770
4179
  if (typeof value !== "string" || value !== "" && Number.isNaN(Date.parse(value))) {
3771
- return [{ fieldPath: path202, message: "Expected a valid date/time string" }];
4180
+ return [{ fieldPath: path212, message: "Expected a valid date/time string" }];
3772
4181
  }
3773
4182
  return [];
3774
4183
  case "select": {
3775
4184
  if (typeof value !== "string") {
3776
- return [{ fieldPath: path202, message: "Expected a selection" }];
4185
+ return [{ fieldPath: path212, message: "Expected a selection" }];
3777
4186
  }
3778
4187
  const options = selectOptionValues(field);
3779
4188
  if (value !== "" && !options.includes(value)) {
3780
- return [{ fieldPath: path202, message: `Must be one of: ${options.join(", ")}` }];
4189
+ return [{ fieldPath: path212, message: `Must be one of: ${options.join(", ")}` }];
3781
4190
  }
3782
4191
  return [];
3783
4192
  }
3784
4193
  case "reference":
3785
4194
  if (referenceValueId(value) === void 0) {
3786
- return [{ fieldPath: path202, message: "Expected a reference id" }];
4195
+ return [{ fieldPath: path212, message: "Expected a reference id" }];
3787
4196
  }
3788
4197
  return [];
3789
4198
  case "image":
3790
- return validateImageValue(field, value, path202);
4199
+ return validateImageValue(field, value, path212);
3791
4200
  default:
3792
4201
  if (STRING_FIELD_TYPES.has(field.type)) {
3793
4202
  if (typeof value !== "string") {
3794
- return [{ fieldPath: path202, message: "Expected text" }];
4203
+ return [{ fieldPath: path212, message: "Expected text" }];
3795
4204
  }
3796
4205
  }
3797
4206
  return [];
@@ -3814,12 +4223,12 @@ function validateEntryData(fields, data, pathPrefix = "") {
3814
4223
  errors.push(...validateEntryData(field.fields, data, pathPrefix));
3815
4224
  continue;
3816
4225
  }
3817
- const path202 = pathPrefix ? `${pathPrefix}.${field.name}` : field.name;
4226
+ const path212 = pathPrefix ? `${pathPrefix}.${field.name}` : field.name;
3818
4227
  const value = data[field.name];
3819
4228
  const isRequired = "required" in field && field.required === true;
3820
4229
  if (isRequired && isEmptyForRequired(field, value)) {
3821
4230
  const message = Array.isArray(value) ? "At least one item is required" : "This field is required";
3822
- errors.push({ fieldPath: path202, message });
4231
+ errors.push({ fieldPath: path212, message });
3823
4232
  continue;
3824
4233
  }
3825
4234
  if (value === void 0 || value === null)
@@ -3827,12 +4236,12 @@ function validateEntryData(fields, data, pathPrefix = "") {
3827
4236
  if (field.type === "block") {
3828
4237
  const blockField = field;
3829
4238
  if (!Array.isArray(value)) {
3830
- errors.push({ fieldPath: path202, message: "Expected a list of blocks" });
4239
+ errors.push({ fieldPath: path212, message: "Expected a list of blocks" });
3831
4240
  continue;
3832
4241
  }
3833
4242
  value.forEach((item, index) => {
3834
- const itemPath = `${path202}[${index}]`;
3835
- if (!isPlainRecord2(item)) {
4243
+ const itemPath = `${path212}[${index}]`;
4244
+ if (!isPlainRecord3(item)) {
3836
4245
  errors.push({ fieldPath: itemPath, message: "Expected a block object" });
3837
4246
  return;
3838
4247
  }
@@ -3853,12 +4262,12 @@ function validateEntryData(fields, data, pathPrefix = "") {
3853
4262
  const objectField = field;
3854
4263
  if (objectField.list) {
3855
4264
  if (!Array.isArray(value)) {
3856
- errors.push({ fieldPath: path202, message: "Expected a list of items" });
4265
+ errors.push({ fieldPath: path212, message: "Expected a list of items" });
3857
4266
  continue;
3858
4267
  }
3859
4268
  value.forEach((item, index) => {
3860
- const itemPath = `${path202}[${index}]`;
3861
- if (!isPlainRecord2(item)) {
4269
+ const itemPath = `${path212}[${index}]`;
4270
+ if (!isPlainRecord3(item)) {
3862
4271
  errors.push({ fieldPath: itemPath, message: "Expected an object" });
3863
4272
  return;
3864
4273
  }
@@ -3866,28 +4275,28 @@ function validateEntryData(fields, data, pathPrefix = "") {
3866
4275
  errors.push(...validateEntryData(objectField.fields, item, itemPath));
3867
4276
  }
3868
4277
  });
3869
- } else if (!isPlainRecord2(value)) {
3870
- errors.push({ fieldPath: path202, message: "Expected an object" });
4278
+ } else if (!isPlainRecord3(value)) {
4279
+ errors.push({ fieldPath: path212, message: "Expected an object" });
3871
4280
  } else if (objectField.fields) {
3872
- errors.push(...validateEntryData(objectField.fields, value, path202));
4281
+ errors.push(...validateEntryData(objectField.fields, value, path212));
3873
4282
  }
3874
4283
  continue;
3875
4284
  }
3876
4285
  if ("list" in field && field.list) {
3877
4286
  if (!Array.isArray(value)) {
3878
- errors.push({ fieldPath: path202, message: "Expected a list of values" });
4287
+ errors.push({ fieldPath: path212, message: "Expected a list of values" });
3879
4288
  continue;
3880
4289
  }
3881
4290
  value.forEach((item, index) => {
3882
- errors.push(...validateScalar(field, item, `${path202}[${index}]`));
4291
+ errors.push(...validateScalar(field, item, `${path212}[${index}]`));
3883
4292
  });
3884
4293
  continue;
3885
4294
  }
3886
4295
  if (Array.isArray(value) && KNOWN_FIELD_TYPES.has(field.type)) {
3887
- errors.push({ fieldPath: path202, message: "Expected a single value, not a list" });
4296
+ errors.push({ fieldPath: path212, message: "Expected a single value, not a list" });
3888
4297
  continue;
3889
4298
  }
3890
- errors.push(...validateScalar(field, value, path202));
4299
+ errors.push(...validateScalar(field, value, path212));
3891
4300
  }
3892
4301
  return errors;
3893
4302
  }
@@ -3931,6 +4340,37 @@ function assertBuildEntriesValid(items, phaseLabel) {
3931
4340
  ${lines.join("\n")}
3932
4341
  These are likely abandoned create-scaffolds (empty entries left behind when a create was started but never finished). Finish editing the entry (fill in its required fields) or delete the abandoned draft, then rebuild.`);
3933
4342
  }
4343
+ async function readGeneratedRecord(outputDir) {
4344
+ try {
4345
+ const raw = await fs17.readFile(path19.join(outputDir, GENERATED_RECORD_FILENAME), "utf-8");
4346
+ const parsed = JSON.parse(raw);
4347
+ if (typeof parsed !== "object" || parsed === null || !("files" in parsed))
4348
+ return [];
4349
+ const { files } = parsed;
4350
+ if (!Array.isArray(files))
4351
+ return [];
4352
+ return files.filter((entry) => typeof entry === "string");
4353
+ } catch {
4354
+ return [];
4355
+ }
4356
+ }
4357
+ async function writeGeneratedRecord(outputDir, files) {
4358
+ const record = { files: [...files].sort() };
4359
+ await fs17.mkdir(outputDir, { recursive: true });
4360
+ await fs17.writeFile(path19.join(outputDir, GENERATED_RECORD_FILENAME), `${JSON.stringify(record, null, 2)}
4361
+ `, "utf-8");
4362
+ }
4363
+ async function removeEmptyDirsUpward(startDir, stopAt) {
4364
+ let current = startDir;
4365
+ while (current.startsWith(stopAt) && current !== stopAt) {
4366
+ try {
4367
+ await fs17.rmdir(current);
4368
+ } catch {
4369
+ return;
4370
+ }
4371
+ current = path19.dirname(current);
4372
+ }
4373
+ }
3934
4374
  async function generateAIContentFiles(options) {
3935
4375
  const { config, entrySchemaRegistry, outputDir, aiConfig: aiConfig2, _testFlatSchema } = options;
3936
4376
  const contentRootName = config.contentRoot || "content";
@@ -3953,23 +4393,45 @@ async function generateAIContentFiles(options) {
3953
4393
  config: aiConfig2,
3954
4394
  entryLinkUrl: config.entryLinkUrl
3955
4395
  });
3956
- const absoluteOutputDir = path18.resolve(outputDir) + path18.sep;
3957
- let fileCount = 0;
3958
- for (const [filePath, content] of result.files) {
3959
- const absolutePath = path18.resolve(path18.join(absoluteOutputDir, filePath));
4396
+ const absoluteOutputDir = path19.resolve(outputDir) + path19.sep;
4397
+ const resolveOutputPath = (filePath) => {
4398
+ const absolutePath = path19.resolve(path19.join(absoluteOutputDir, filePath));
3960
4399
  if (!absolutePath.startsWith(absoluteOutputDir)) {
3961
4400
  throw new Error(`Path traversal detected in AI content output: ${filePath}`);
3962
4401
  }
3963
- await fs16.mkdir(path18.dirname(absolutePath), { recursive: true });
3964
- await fs16.writeFile(absolutePath, content, "utf-8");
4402
+ return absolutePath;
4403
+ };
4404
+ const previousFiles = await readGeneratedRecord(absoluteOutputDir);
4405
+ const currentFiles = [...result.files.keys()];
4406
+ await writeGeneratedRecord(absoluteOutputDir, [.../* @__PURE__ */ new Set([...previousFiles, ...currentFiles])]);
4407
+ let fileCount = 0;
4408
+ for (const [filePath, content] of result.files) {
4409
+ const absolutePath = resolveOutputPath(filePath);
4410
+ await fs17.mkdir(path19.dirname(absolutePath), { recursive: true });
4411
+ await fs17.writeFile(absolutePath, content, "utf-8");
3965
4412
  fileCount++;
3966
4413
  }
3967
- return { fileCount, outputDir: absoluteOutputDir };
4414
+ const currentSet = new Set(currentFiles);
4415
+ const stale = previousFiles.filter((filePath) => !currentSet.has(filePath));
4416
+ let removedCount = 0;
4417
+ for (const filePath of stale) {
4418
+ const absolutePath = resolveOutputPath(filePath);
4419
+ try {
4420
+ await fs17.unlink(absolutePath);
4421
+ removedCount++;
4422
+ } catch (err) {
4423
+ if (!isNodeError3(err) || err.code !== "ENOENT")
4424
+ throw err;
4425
+ }
4426
+ await removeEmptyDirsUpward(path19.dirname(absolutePath), absoluteOutputDir);
4427
+ }
4428
+ await writeGeneratedRecord(absoluteOutputDir, currentFiles);
4429
+ return { fileCount, removedCount, outputDir: absoluteOutputDir };
3968
4430
  }
3969
4431
  async function generateAIContentCLI(options) {
3970
4432
  const { projectDir, outputDir = "public/ai", configPath, appDir = "app" } = options;
3971
4433
  console.log("\nCanopyCMS generate-ai-content\n");
3972
- const canopyConfigPath = path19.join(projectDir, "canopycms.config.ts");
4434
+ const canopyConfigPath = path20.join(projectDir, "canopycms.config.ts");
3973
4435
  let canopyConfigModule;
3974
4436
  try {
3975
4437
  canopyConfigModule = await jiti.import(canopyConfigPath);
@@ -3980,7 +4442,7 @@ async function generateAIContentCLI(options) {
3980
4442
  }
3981
4443
  const configExport = canopyConfigModule.default ?? canopyConfigModule.config ?? canopyConfigModule;
3982
4444
  const serverConfig = typeof configExport === "object" && configExport !== null && "server" in configExport ? configExport.server : configExport;
3983
- const schemasPath = path19.join(projectDir, appDir, "schemas.ts");
4445
+ const schemasPath = path20.join(projectDir, appDir, "schemas.ts");
3984
4446
  let entrySchemaRegistry = {};
3985
4447
  try {
3986
4448
  const schemasModule = await jiti.import(schemasPath);
@@ -3991,7 +4453,7 @@ async function generateAIContentCLI(options) {
3991
4453
  let aiConfig2;
3992
4454
  if (configPath) {
3993
4455
  try {
3994
- const aiConfigModule = await jiti.import(path19.resolve(configPath));
4456
+ const aiConfigModule = await jiti.import(path20.resolve(configPath));
3995
4457
  aiConfig2 = aiConfigModule.aiContentConfig ?? aiConfigModule.default ?? aiConfigModule.config;
3996
4458
  } catch (err) {
3997
4459
  console.error(`Could not load AI config from ${configPath}`);
@@ -4008,7 +4470,7 @@ async function generateAIContentCLI(options) {
4008
4470
  console.error("Make sure canopycms.config.ts uses defineCanopyConfig().");
4009
4471
  process.exit(1);
4010
4472
  }
4011
- const resolvedOutput = path19.resolve(projectDir, outputDir);
4473
+ const resolvedOutput = path20.resolve(projectDir, outputDir);
4012
4474
  console.log(` Output: ${resolvedOutput}`);
4013
4475
  console.log(` Mode: ${serverConfig.mode ?? "dev"}`);
4014
4476
  const result = await generateAIContentFiles({
@@ -4019,10 +4481,13 @@ async function generateAIContentCLI(options) {
4019
4481
  });
4020
4482
  console.log(`
4021
4483
  Generated ${result.fileCount} files`);
4484
+ if (result.removedCount > 0) {
4485
+ console.log(` Removed ${result.removedCount} stale files from the previous run`);
4486
+ }
4022
4487
  console.log(` Output: ${result.outputDir}
4023
4488
  `);
4024
4489
  }
4025
- var __defProp3, __getOwnPropNames3, __esm3, __export3, init_normalize2, active2, init_logger2, ROOT_COLLECTION_ID, init_types3, normalizePathValue, flattenSchema, init_flatten2, primitiveFieldTypes2, fieldTypes2, init_types22, fieldBaseSchema2, selectOptionSchema2, referenceOptionSchema2, primitiveFieldSchema2, ASPECT_RATIO_RE2, imageFieldSchema2, selectFieldSchema2, referenceFieldSchema2, fieldHolder2, blockSchema2, blockFieldSchema2, objectFieldSchema2, inlineGroupFieldSchema2, customFieldSchema2, knownFieldSchema2, fieldSchema2, init_field2, relativePathSchema2, entryTypeSchema2, collectionSchema2, init_collection2, mediaSchema2, init_media2, defaultBranchAccessSchema2, defaultPathAccessLevelSchema2, defaultPathAccessLevelsSchema2, defaultPathAccessSchema2, defaultBaseBranchSchema2, defaultRemoteNameSchema2, defaultRemoteUrlSchema2, gitBotAuthorNameSchema2, gitBotAuthorEmailSchema2, githubTokenEnvVarSchema2, operatingModeSchema2, deployedAsSchema2, contentRootSchema2, sourceRootSchema2, deploymentNameSchema2, devContentSyncSchema2, devConfigSchema2, editorConfigSchema2, CanopyConfigSchema2, DEFAULT_PROD_WORKSPACE2, init_config3, SERVER_MODE_ENV_VAR, BROWSER_MODE_ENV_VAR, warned2, init_mode_env2, forEachReferenceField, init_validation2, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned22, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, permissionTargetSchema2, pathPermissionSchema2, init_permissions2, init_helpers2, init_config22, ProdStrategy2, DevStrategy2, strategyCache2, init_client_unsafe_strategy, operating_mode_exports, init_operating_mode, locks, META_DIR, CONTENT_WRITE_LOCK_NAME, DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, ContentWriteLockBusyError, sleep, BASE58_PATTERN, CONTENT_ID_PATTERN, PHYSICAL_SEGMENT_PATTERN, isValidId, EMPTY_LOGICAL_PATH, ContentIdIndex, registry, finalization, LOG_LEVELS2, DebugLogger2, testLogger2, log2, META_DIR2, entryTypeMetaSchema, collectionMetaSchema, rootCollectionMetaSchema, editDistance, closestName, forEachEntryType, collectEntryTypeNames, validateReferenceEntryTypes, isDeployedStatic, isBuildMode, STATIC_DEPLOY_USER, SCHEMA_CACHE_VERSION, MTIME_CHECK_DEBOUNCE_MS, SCHEMA_GENERATION_RESOURCE, BranchSchemaCache, RESOURCE, getFormatExtension, isDataOnlyFormat, asRecord, BranchPathError, resolveContentBranchesRoot, ContentStoreError, ContentConflictError, BranchSyncingError, DuplicateContentIdError, DEFAULT_INDEX_FRESHNESS_INTERVAL_MS, FORCED_REFRESH_MIN_INTERVAL_MS, STALE_LOOKUP, ContentStore, ATTR_CONTENT, BLOCK_PREFIX, BLOCK_SUFFIX, BLOCK_RESTORE_RE, INLINE_PREFIX, INLINE_SUFFIX, INLINE_RESTORE_RE, FLAT_CELL_TYPES, log22, BASE58_CHAR, ENTRY_LINK_PATTERN, log3, REGISTRY_FILE, REGISTRY_STALE_FILE, REGISTRY_TEMP_FILE, REGISTRY_VERSION, RESOURCE2, GET_MISS_REFRESH_MIN_INTERVAL_MS, BranchRegistry, log4, OccWriteConflictError, BRANCH_META_DIR, BRANCH_META_FILE, CURRENT_SCHEMA_VERSION, BranchMetadataConflictError, BranchMetadataCorruptError, BranchMetadataFileManager, getBranchMetadataFileManager, loadBranchContext, NETWORK_SCHEME_PATTERN, TRANSPORT_HELPER_PATTERN, LEADING_OPTION_PATTERN, WINDOWS_DRIVE_PATTERN, COLON_BEFORE_SLASH_PATTERN, MISSING_REMOTE_REF_REASONS, log5, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, GitConflictError, GitRemoteRefMissingError, GitManager, log6, workspaceInitLocks, BranchWorkspaceManager, log7, normalizePath, readEntryData, parseTypedFilename, listCollectionEntries, MAX_INPUT_PIXELS, KNOWN_FIELD_TYPES, STRING_FIELD_TYPES, selectOptionValues, jiti;
4490
+ var __defProp3, __getOwnPropNames3, __esm3, __export3, active2, init_logger2, init_normalize2, ROOT_COLLECTION_ID, init_types3, normalizePathValue, flattenSchema, init_flatten2, primitiveFieldTypes2, fieldTypes2, init_types22, fieldBaseSchema2, selectOptionSchema2, referenceOptionSchema2, primitiveFieldSchema2, ASPECT_RATIO_RE2, imageFieldSchema2, selectFieldSchema2, referenceFieldSchema2, fieldHolder2, blockSchema2, blockFieldSchema2, objectFieldSchema2, inlineGroupFieldSchema2, customFieldSchema2, knownFieldSchema2, fieldSchema2, init_field2, relativePathSchema2, entryTypeSchema2, collectionSchema2, init_collection2, mediaSchema2, init_media2, defaultBranchAccessSchema2, defaultPathAccessLevelSchema2, defaultPathAccessLevelsSchema2, defaultPathAccessSchema2, defaultBaseBranchSchema2, defaultRemoteNameSchema2, defaultRemoteUrlSchema2, gitBotAuthorNameSchema2, gitBotAuthorEmailSchema2, githubTokenEnvVarSchema2, operatingModeSchema2, deployedAsSchema2, contentRootSchema2, sourceRootSchema2, basePathSchema2, deploymentNameSchema2, devContentSyncSchema2, devConfigSchema2, editorConfigSchema2, CanopyConfigSchema2, DEFAULT_PROD_WORKSPACE2, init_config3, SERVER_MODE_ENV_VAR, BROWSER_MODE_ENV_VAR, warned2, init_mode_env2, forEachReferenceField, init_validation2, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned22, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, permissionTargetSchema2, pathPermissionSchema2, init_permissions2, init_helpers2, init_config22, ProdStrategy2, DevStrategy2, strategyCache2, init_client_unsafe_strategy, operating_mode_exports, init_operating_mode, BLOCK_DISCRIMINATOR_KEYS, BLOCK_STRUCTURAL_KEYS, EVIDENCE_MAX_DEPTH, locks, META_DIR, CONTENT_WRITE_LOCK_NAME, DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, ContentWriteLockBusyError, sleep, DEFAULT_SEO_FIELD_NAMES, SEO_GROUP_FIELDS, BASE58_PATTERN, CONTENT_ID_PATTERN, PHYSICAL_SEGMENT_PATTERN, isValidId, EMPTY_LOGICAL_PATH, ContentIdIndex, parseTypedFilename, CONTENT_EXTENSIONS, registry, finalization, LOG_LEVELS2, DebugLogger2, testLogger2, log2, META_DIR2, entryTypeMetaSchema, collectionMetaSchema, rootCollectionMetaSchema, editDistance, closestName, forEachEntryType, collectEntryTypeNames, validateReferenceEntryTypes, isDeployedStatic, isBuildMode, STATIC_DEPLOY_USER, SCHEMA_CACHE_VERSION, MTIME_CHECK_DEBOUNCE_MS, SCHEMA_GENERATION_RESOURCE, BranchSchemaCache, RESOURCE, getFormatExtension, isDataOnlyFormat, asRecord, BranchPathError, resolveContentBranchesRoot, ContentStoreError, ContentConflictError, BranchSyncingError, DuplicateContentIdError, UrlPathConflictError, DEFAULT_INDEX_FRESHNESS_INTERVAL_MS, FORCED_REFRESH_MIN_INTERVAL_MS, STALE_LOOKUP, createReferenceResolveCache, ContentStore, ATTR_CONTENT, BLOCK_PREFIX, BLOCK_SUFFIX, BLOCK_RESTORE_RE, INLINE_PREFIX, INLINE_SUFFIX, INLINE_RESTORE_RE, FLAT_CELL_TYPES, log22, BASE58_CHAR, ENTRY_LINK_PATTERN, log3, REGISTRY_FILE, REGISTRY_STALE_FILE, REGISTRY_TEMP_FILE, REGISTRY_VERSION, RESOURCE2, GET_MISS_REFRESH_MIN_INTERVAL_MS, BranchRegistry, log4, OccWriteConflictError, BRANCH_META_DIR, BRANCH_META_FILE, CURRENT_SCHEMA_VERSION, BranchMetadataConflictError, BranchMetadataCorruptError, BranchMetadataFileManager, getBranchMetadataFileManager, loadBranchContext, NETWORK_SCHEME_PATTERN, TRANSPORT_HELPER_PATTERN, LEADING_OPTION_PATTERN, WINDOWS_DRIVE_PATTERN, COLON_BEFORE_SLASH_PATTERN, MISSING_REMOTE_REF_REASONS, log5, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, GitConflictError, GitRemoteRefMissingError, GitManager, log6, workspaceInitLocks, BranchWorkspaceManager, log7, normalizePath, readEntryData, createReferenceResolver, resolveCollectionItemReferences, looksLikeMalformedEntry, listCollectionEntries, MAX_INPUT_PIXELS, KNOWN_FIELD_TYPES, STRING_FIELD_TYPES, selectOptionValues, GENERATED_RECORD_FILENAME, jiti;
4026
4491
  var init_generate_ai_content = __esm({
4027
4492
  "dist/cli/generate-ai-content.js"() {
4028
4493
  "use strict";
@@ -4035,17 +4500,17 @@ var init_generate_ai_content = __esm({
4035
4500
  for (var name in all)
4036
4501
  __defProp3(target, name, { get: all[name], enumerable: true });
4037
4502
  };
4038
- init_normalize2 = __esm3({
4039
- "dist/paths/normalize.js"() {
4040
- "use strict";
4041
- }
4042
- });
4043
4503
  init_logger2 = __esm3({
4044
4504
  "dist/utils/logger.js"() {
4045
4505
  "use strict";
4046
4506
  active2 = console;
4047
4507
  }
4048
4508
  });
4509
+ init_normalize2 = __esm3({
4510
+ "dist/paths/normalize.js"() {
4511
+ "use strict";
4512
+ }
4513
+ });
4049
4514
  init_types3 = __esm3({
4050
4515
  "dist/paths/types.js"() {
4051
4516
  "use strict";
@@ -4219,7 +4684,12 @@ var init_generate_ai_content = __esm({
4219
4684
  collections: z22.array(z22.string().min(1)).min(1).optional(),
4220
4685
  entryTypes: z22.array(z22.string().min(1)).min(1).optional(),
4221
4686
  displayField: z22.string().min(1).optional(),
4222
- options: z22.array(referenceOptionSchema2).optional()
4687
+ options: z22.array(referenceOptionSchema2).optional(),
4688
+ // Must be listed here, not just on ReferenceFieldConfig: zod strips unknown keys by
4689
+ // default, so a consumer that adopts this schema's parse output would silently delete a
4690
+ // runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
4691
+ // absent on purpose — it is type-inference-only and stripping it changes no behavior.)
4692
+ includeBody: z22.boolean().optional()
4223
4693
  });
4224
4694
  fieldHolder2 = [z22.never()];
4225
4695
  blockSchema2 = z22.object({
@@ -4340,6 +4810,9 @@ var init_generate_ai_content = __esm({
4340
4810
  deployedAsSchema2 = z52.enum(["static", "server"]).default("server");
4341
4811
  contentRootSchema2 = relativePathSchema2.default("content");
4342
4812
  sourceRootSchema2 = z52.string().min(1).optional();
4813
+ basePathSchema2 = z52.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
4814
+ message: "basePath must not be empty or only whitespace"
4815
+ }).optional();
4343
4816
  deploymentNameSchema2 = z52.string().default("prod");
4344
4817
  devContentSyncSchema2 = z52.enum(["off", "warn"]).default("warn");
4345
4818
  devConfigSchema2 = z52.object({
@@ -4402,6 +4875,7 @@ var init_generate_ai_content = __esm({
4402
4875
  deploymentName: deploymentNameSchema2.optional(),
4403
4876
  contentRoot: contentRootSchema2.default("content"),
4404
4877
  sourceRoot: sourceRootSchema2.optional(),
4878
+ basePath: basePathSchema2,
4405
4879
  editor: editorConfigSchema2.optional(),
4406
4880
  authPlugin: z52.custom().optional(),
4407
4881
  entryLinkUrl: z52.custom().optional(),
@@ -4571,32 +5045,32 @@ var init_generate_ai_content = __esm({
4571
5045
  // - shouldCommit(), shouldPush()
4572
5046
  // Add client-unsafe methods (use Node.js APIs)
4573
5047
  getWorkspaceRoot(_sourceRoot) {
4574
- return path82.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE2);
5048
+ return path9.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE2);
4575
5049
  }
4576
5050
  getContentRoot(contentRoot, sourceRoot) {
4577
- return path82.resolve(sourceRoot ?? process.cwd(), contentRoot);
5051
+ return path9.resolve(sourceRoot ?? process.cwd(), contentRoot);
4578
5052
  }
4579
5053
  getContentBranchesRoot(sourceRoot) {
4580
- return path82.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
5054
+ return path9.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
4581
5055
  }
4582
5056
  getContentBranchRoot(branchName, sourceRoot) {
4583
- return path82.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
5057
+ return path9.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
4584
5058
  }
4585
5059
  getGitExcludePattern() {
4586
5060
  return ".canopy-meta/";
4587
5061
  }
4588
5062
  getPermissionsFilePath(root) {
4589
- return path82.join(root, this.getPermissionsFileName());
5063
+ return path9.join(root, this.getPermissionsFileName());
4590
5064
  }
4591
5065
  getGroupsFilePath(root) {
4592
- return path82.join(root, this.getGroupsFileName());
5066
+ return path9.join(root, this.getGroupsFileName());
4593
5067
  }
4594
5068
  getRemoteUrlConfig() {
4595
5069
  return {
4596
5070
  shouldAutoInitLocal: false,
4597
5071
  defaultRemotePath: "",
4598
5072
  envVarName: "CANOPYCMS_REMOTE_URL",
4599
- autoDetectRemotePath: path82.join(this.getWorkspaceRoot(), "remote.git")
5073
+ autoDetectRemotePath: path9.join(this.getWorkspaceRoot(), "remote.git")
4600
5074
  };
4601
5075
  }
4602
5076
  requiresExistingRepo() {
@@ -4608,7 +5082,7 @@ var init_generate_ai_content = __esm({
4608
5082
  return `canopycms-settings-${resolveDeploymentName2(config, "prod")}`;
4609
5083
  }
4610
5084
  getSettingsRoot(sourceRoot) {
4611
- return path82.join(this.getWorkspaceRoot(sourceRoot), "settings");
5085
+ return path9.join(this.getWorkspaceRoot(sourceRoot), "settings");
4612
5086
  }
4613
5087
  usesSeparateSettingsBranch() {
4614
5088
  return true;
@@ -4625,25 +5099,25 @@ var init_generate_ai_content = __esm({
4625
5099
  DevStrategy2 = class extends DevClientSafeStrategy2 {
4626
5100
  // Inherits client-safe methods from DevClientSafeStrategy
4627
5101
  getWorkspaceRoot(sourceRoot) {
4628
- return path82.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
5102
+ return path9.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
4629
5103
  }
4630
5104
  getContentRoot(contentRoot, sourceRoot) {
4631
- return path82.resolve(sourceRoot ?? process.cwd(), contentRoot);
5105
+ return path9.resolve(sourceRoot ?? process.cwd(), contentRoot);
4632
5106
  }
4633
5107
  getContentBranchesRoot(sourceRoot) {
4634
- return path82.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
5108
+ return path9.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
4635
5109
  }
4636
5110
  getContentBranchRoot(branchName, sourceRoot) {
4637
- return path82.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
5111
+ return path9.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
4638
5112
  }
4639
5113
  getGitExcludePattern() {
4640
5114
  return ".canopy-meta/";
4641
5115
  }
4642
5116
  getPermissionsFilePath(root) {
4643
- return path82.join(root, this.getPermissionsFileName());
5117
+ return path9.join(root, this.getPermissionsFileName());
4644
5118
  }
4645
5119
  getGroupsFilePath(root) {
4646
- return path82.join(root, this.getGroupsFileName());
5120
+ return path9.join(root, this.getGroupsFileName());
4647
5121
  }
4648
5122
  getRemoteUrlConfig() {
4649
5123
  return {
@@ -4661,7 +5135,7 @@ var init_generate_ai_content = __esm({
4661
5135
  return `canopycms-settings-${resolveDeploymentName2(config, "local")}`;
4662
5136
  }
4663
5137
  getSettingsRoot(sourceRoot) {
4664
- return path82.join(this.getWorkspaceRoot(sourceRoot), "settings");
5138
+ return path9.join(this.getWorkspaceRoot(sourceRoot), "settings");
4665
5139
  }
4666
5140
  usesSeparateSettingsBranch() {
4667
5141
  return true;
@@ -4696,7 +5170,12 @@ var init_generate_ai_content = __esm({
4696
5170
  init_mode_env2();
4697
5171
  }
4698
5172
  });
5173
+ BLOCK_DISCRIMINATOR_KEYS = ["template", "_type"];
5174
+ BLOCK_STRUCTURAL_KEYS = new Set(BLOCK_DISCRIMINATOR_KEYS);
5175
+ EVIDENCE_MAX_DEPTH = 6;
4699
5176
  locks = /* @__PURE__ */ new Map();
5177
+ init_logger2();
5178
+ init_logger2();
4700
5179
  META_DIR = ".canopy-meta";
4701
5180
  CONTENT_WRITE_LOCK_NAME = "content-write.lock";
4702
5181
  DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS = 2e3;
@@ -4709,6 +5188,67 @@ var init_generate_ai_content = __esm({
4709
5188
  sleep = (ms) => new Promise((resolve2) => {
4710
5189
  setTimeout(resolve2, ms);
4711
5190
  });
5191
+ DEFAULT_SEO_FIELD_NAMES = {
5192
+ title: "metaTitle",
5193
+ description: "metaDescription",
5194
+ ogImage: "ogImage",
5195
+ ogType: "ogType",
5196
+ canonical: "canonical",
5197
+ noindex: "noindex",
5198
+ twitterCard: "twitterCard"
5199
+ };
5200
+ SEO_GROUP_FIELDS = [
5201
+ {
5202
+ name: DEFAULT_SEO_FIELD_NAMES.title,
5203
+ type: "string",
5204
+ label: "Meta Title",
5205
+ description: "Overrides the page title in search results and social cards.",
5206
+ required: false
5207
+ },
5208
+ {
5209
+ name: DEFAULT_SEO_FIELD_NAMES.description,
5210
+ type: "string",
5211
+ label: "Meta Description",
5212
+ description: "Summary shown under the title in search results.",
5213
+ required: false
5214
+ },
5215
+ {
5216
+ name: DEFAULT_SEO_FIELD_NAMES.ogImage,
5217
+ type: "string",
5218
+ label: "Social Image URL",
5219
+ description: "Image for social cards. Site-relative or absolute.",
5220
+ required: false
5221
+ },
5222
+ {
5223
+ name: DEFAULT_SEO_FIELD_NAMES.ogType,
5224
+ type: "select",
5225
+ label: "OpenGraph Type",
5226
+ options: ["website", "article", "profile"],
5227
+ required: false
5228
+ },
5229
+ {
5230
+ name: DEFAULT_SEO_FIELD_NAMES.canonical,
5231
+ type: "string",
5232
+ label: "Canonical URL",
5233
+ description: "Set only to point at a different canonical copy of this page.",
5234
+ required: false
5235
+ },
5236
+ {
5237
+ name: DEFAULT_SEO_FIELD_NAMES.noindex,
5238
+ type: "boolean",
5239
+ label: "Hide from search engines",
5240
+ description: "Marks the page noindex AND drops it from the sitemap.",
5241
+ required: false
5242
+ },
5243
+ {
5244
+ name: DEFAULT_SEO_FIELD_NAMES.twitterCard,
5245
+ type: "select",
5246
+ label: "Twitter Card",
5247
+ options: ["summary", "summary_large_image"],
5248
+ required: false
5249
+ }
5250
+ ];
5251
+ init_normalize2();
4712
5252
  init_normalize2();
4713
5253
  BASE58_PATTERN = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]";
4714
5254
  CONTENT_ID_PATTERN = new RegExp(`^${BASE58_PATTERN}{12}$`);
@@ -5026,6 +5566,31 @@ var init_generate_ai_content = __esm({
5026
5566
  this.pathToId.set(newRelativePath, id);
5027
5567
  }
5028
5568
  };
5569
+ parseTypedFilename = (filename, entryTypes) => {
5570
+ if (filename.startsWith("."))
5571
+ return null;
5572
+ const lastDot = filename.lastIndexOf(".");
5573
+ if (lastDot === -1)
5574
+ return null;
5575
+ const nameWithoutExt = filename.slice(0, lastDot);
5576
+ const parts = nameWithoutExt.split(".");
5577
+ if (parts.length < 3)
5578
+ return null;
5579
+ const potentialType = parts[0];
5580
+ if (entryTypes && !entryTypes.some((e) => e.name === potentialType)) {
5581
+ return null;
5582
+ }
5583
+ const id = parts[parts.length - 1];
5584
+ if (!isValidId(id))
5585
+ return null;
5586
+ const slug = parts.slice(1, -1).join(".").toLowerCase();
5587
+ return {
5588
+ type: potentialType,
5589
+ slug,
5590
+ id
5591
+ };
5592
+ };
5593
+ CONTENT_EXTENSIONS = [".md", ".mdx", ".json", ".yaml"];
5029
5594
  registry = /* @__PURE__ */ new Map();
5030
5595
  finalization = new FinalizationRegistry(({ rootKey, ref }) => {
5031
5596
  const refs = registry.get(rootKey);
@@ -5263,7 +5828,7 @@ var init_generate_ai_content = __esm({
5263
5828
  * the project root, regardless of which entrypoint produced the cwd branchRoot.
5264
5829
  */
5265
5830
  skipDiskCache(branchRoot) {
5266
- return isBuildMode() || path72.resolve(branchRoot) === path72.resolve(process.cwd());
5831
+ return isBuildMode() || path82.resolve(branchRoot) === path82.resolve(process.cwd());
5267
5832
  }
5268
5833
  /**
5269
5834
  * Get schema for a branch (loads from cache or resolves fresh).
@@ -5289,14 +5854,14 @@ var init_generate_ai_content = __esm({
5289
5854
  * Load schema from cache or resolve fresh if cache is missing or stale.
5290
5855
  */
5291
5856
  async loadFromCacheOrResolve(branchRoot, entrySchemaRegistry, contentRootName) {
5292
- const contentRoot = path72.join(branchRoot, contentRootName);
5857
+ const contentRoot = path82.join(branchRoot, contentRootName);
5293
5858
  const skipDiskCache = this.skipDiskCache(branchRoot);
5294
5859
  if (!skipDiskCache) {
5295
- const cacheDir = path72.join(branchRoot, ".canopy-meta");
5296
- const cachePath = path72.join(cacheDir, "schema-cache.json");
5860
+ const cacheDir = path82.join(branchRoot, ".canopy-meta");
5861
+ const cachePath = path82.join(cacheDir, "schema-cache.json");
5297
5862
  let cacheData = null;
5298
5863
  try {
5299
- const cacheContent = await fs62.readFile(cachePath, "utf-8");
5864
+ const cacheContent = await fs72.readFile(cachePath, "utf-8");
5300
5865
  cacheData = JSON.parse(cacheContent);
5301
5866
  } catch {
5302
5867
  cacheData = null;
@@ -5333,7 +5898,7 @@ var init_generate_ai_content = __esm({
5333
5898
  */
5334
5899
  async resolveFreshAndPersist(branchRoot, entrySchemaRegistry, contentRootName, options) {
5335
5900
  const { skipDiskCache } = options;
5336
- const contentRoot = path72.join(branchRoot, contentRootName);
5901
+ const contentRoot = path82.join(branchRoot, contentRootName);
5337
5902
  const read = skipDiskCache ? null : await readResourceGeneration(branchRoot, SCHEMA_GENERATION_RESOURCE);
5338
5903
  const result = await this.resolveFresh(contentRoot, entrySchemaRegistry);
5339
5904
  if (!isValidSchema(result.schema)) {
@@ -5346,9 +5911,9 @@ var init_generate_ai_content = __esm({
5346
5911
  }
5347
5912
  const flatSchema = flattenSchema(result.schema, contentRootName);
5348
5913
  if (!skipDiskCache) {
5349
- const cacheDir = path72.join(branchRoot, ".canopy-meta");
5350
- const cachePath = path72.join(cacheDir, "schema-cache.json");
5351
- await fs62.unlink(path72.join(cacheDir, "schema-cache.stale")).catch(() => {
5914
+ const cacheDir = path82.join(branchRoot, ".canopy-meta");
5915
+ const cachePath = path82.join(cacheDir, "schema-cache.json");
5916
+ await fs72.unlink(path82.join(cacheDir, "schema-cache.stale")).catch(() => {
5352
5917
  });
5353
5918
  if (read && read.ok) {
5354
5919
  const newCache = {
@@ -5358,13 +5923,13 @@ var init_generate_ai_content = __esm({
5358
5923
  cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
5359
5924
  generation: read.token
5360
5925
  };
5361
- await fs62.mkdir(cacheDir, { recursive: true });
5362
- const tmpPath = path72.join(cacheDir, `schema-cache.tmp.${Date.now()}.${Math.random()}.json`);
5363
- await fs62.writeFile(tmpPath, JSON.stringify(newCache, null, 2), "utf-8");
5926
+ await fs72.mkdir(cacheDir, { recursive: true });
5927
+ const tmpPath = path82.join(cacheDir, `schema-cache.tmp.${Date.now()}.${Math.random()}.json`);
5928
+ await fs72.writeFile(tmpPath, JSON.stringify(newCache, null, 2), "utf-8");
5364
5929
  try {
5365
- await fs62.rename(tmpPath, cachePath);
5930
+ await fs72.rename(tmpPath, cachePath);
5366
5931
  } catch (err) {
5367
- await fs62.unlink(tmpPath).catch(() => {
5932
+ await fs72.unlink(tmpPath).catch(() => {
5368
5933
  });
5369
5934
  throw err;
5370
5935
  }
@@ -5474,9 +6039,17 @@ var init_generate_ai_content = __esm({
5474
6039
  this.paths = sorted;
5475
6040
  }
5476
6041
  };
6042
+ UrlPathConflictError = class extends ContentConflictError {
6043
+ constructor(message, conflictingPath) {
6044
+ super(message);
6045
+ this.name = "UrlPathConflictError";
6046
+ this.conflictingPath = conflictingPath;
6047
+ }
6048
+ };
5477
6049
  DEFAULT_INDEX_FRESHNESS_INTERVAL_MS = 1e3;
5478
6050
  FORCED_REFRESH_MIN_INTERVAL_MS = 5e3;
5479
6051
  STALE_LOOKUP = Symbol("stale-index-lookup");
6052
+ createReferenceResolveCache = () => /* @__PURE__ */ new Map();
5480
6053
  ContentStore = class {
5481
6054
  constructor(root, flatSchema, options = {}) {
5482
6055
  this.indexGeneration = 0;
@@ -5485,7 +6058,7 @@ var init_generate_ai_content = __esm({
5485
6058
  this.lastDiskProbeMs = 0;
5486
6059
  this.lastForcedRefreshMs = 0;
5487
6060
  this.indexBuild = null;
5488
- this.root = path10.resolve(root);
6061
+ this.root = path11.resolve(root);
5489
6062
  this.contentRootName = options.contentRootName || "content";
5490
6063
  this.indexFreshnessIntervalMs = options.indexFreshnessIntervalMs ?? DEFAULT_INDEX_FRESHNESS_INTERVAL_MS;
5491
6064
  this.contentWriteLockWaitMs = options.contentWriteLockWaitMs ?? DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS;
@@ -5633,7 +6206,7 @@ var init_generate_ai_content = __esm({
5633
6206
  async findEntryPathsById(dir, id) {
5634
6207
  let entries;
5635
6208
  try {
5636
- entries = await fs9.readdir(dir, { withFileTypes: true });
6209
+ entries = await fs10.readdir(dir, { withFileTypes: true });
5637
6210
  } catch (err) {
5638
6211
  if (isNodeError3(err) && err.code === "ENOENT")
5639
6212
  return [];
@@ -5644,7 +6217,7 @@ var init_generate_ai_content = __esm({
5644
6217
  if (entry.isDirectory())
5645
6218
  continue;
5646
6219
  if (extractIdFromFilename(entry.name) === id) {
5647
- matches.push(path10.relative(this.root, path10.join(dir, entry.name)));
6220
+ matches.push(path11.relative(this.root, path11.join(dir, entry.name)));
5648
6221
  }
5649
6222
  }
5650
6223
  return matches.sort();
@@ -5656,11 +6229,11 @@ var init_generate_ai_content = __esm({
5656
6229
  getSchemaItems() {
5657
6230
  return this.schemaIndex.values();
5658
6231
  }
5659
- assertSchemaItem(path202) {
5660
- const normalized = normalizeFilesystemPath(path202);
6232
+ assertSchemaItem(path212) {
6233
+ const normalized = normalizeFilesystemPath(path212);
5661
6234
  const item = this.schemaIndex.get(normalized);
5662
6235
  if (!item) {
5663
- throw new ContentStoreError(`Unknown schema item: ${path202}`, "NO_SCHEMA_ITEM");
6236
+ throw new ContentStoreError(`Unknown schema item: ${path212}`, "NO_SCHEMA_ITEM");
5664
6237
  }
5665
6238
  return item;
5666
6239
  }
@@ -5727,6 +6300,26 @@ var init_generate_ai_content = __esm({
5727
6300
  return this.createLockKey(schemaItem, slug);
5728
6301
  return prePass.id ? this.idLockKey(prePass.id) : prePass.absolutePath;
5729
6302
  }
6303
+ /**
6304
+ * Refuse a create/rename that would give a second entry a `urlPath` another entry already
6305
+ * holds. See url-collision.ts for the two shapes that count, and the legitimate
6306
+ * landing-page-beside-a-collection shape that deliberately does not.
6307
+ *
6308
+ * @param collectionDir Absolute physical directory the entry will live in.
6309
+ * @param slug The entry's slug, as it will be written.
6310
+ */
6311
+ async assertUrlPathAvailable(collectionDir, slug) {
6312
+ const claimant = await findUrlPathClaimant({
6313
+ collectionDir,
6314
+ slug,
6315
+ contentRoot: path11.resolve(this.root, this.contentRootName)
6316
+ });
6317
+ if (!claimant)
6318
+ return;
6319
+ const relative2 = path11.relative(this.root, claimant.physicalPath);
6320
+ 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.`;
6321
+ throw new UrlPathConflictError(message, claimant.physicalPath);
6322
+ }
5730
6323
  /**
5731
6324
  * Build absolute and relative paths with security validation.
5732
6325
  * All entries use the unified filename pattern: {type}.{slug}.{id}.{ext}
@@ -5743,7 +6336,7 @@ var init_generate_ai_content = __esm({
5743
6336
  * @param options.entryTypeName - For collections with multiple entry types, specify which one to use. Defaults to the default entry type.
5744
6337
  */
5745
6338
  async buildPaths(schemaItem, slug, options = {}) {
5746
- const rootWithSep = this.root.endsWith(path10.sep) ? this.root : `${this.root}${path10.sep}`;
6339
+ const rootWithSep = this.root.endsWith(path11.sep) ? this.root : `${this.root}${path11.sep}`;
5747
6340
  if (schemaItem.type === "entry-type") {
5748
6341
  const parentPath = schemaItem.parentPath || "";
5749
6342
  const parentCollection = this.schemaIndex.get(parentPath);
@@ -5776,7 +6369,7 @@ var init_generate_ai_content = __esm({
5776
6369
  const entryTypeName = entryTypeConfig?.name || "entry";
5777
6370
  let collectionRoot = await resolveCollectionPath(this.root, schemaItem.logicalPath);
5778
6371
  if (!collectionRoot) {
5779
- collectionRoot = path10.resolve(this.root, schemaItem.logicalPath);
6372
+ collectionRoot = path11.resolve(this.root, schemaItem.logicalPath);
5780
6373
  }
5781
6374
  if (!collectionRoot.startsWith(rootWithSep)) {
5782
6375
  throw new ContentStoreError("Path traversal detected", "VALIDATION");
@@ -5786,7 +6379,7 @@ var init_generate_ai_content = __esm({
5786
6379
  let existingEntryType;
5787
6380
  let foundExisting = false;
5788
6381
  if (!id) {
5789
- const entries = await fs9.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
6382
+ const entries = await fs10.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
5790
6383
  const existingFile = entries.find((entry) => {
5791
6384
  if (entry.isDirectory())
5792
6385
  return false;
@@ -5812,14 +6405,14 @@ var init_generate_ai_content = __esm({
5812
6405
  }
5813
6406
  filename = `${finalEntryTypeName}.${safeSlug}.${id}${ext}`;
5814
6407
  }
5815
- const resolved = path10.resolve(collectionRoot, filename);
5816
- const collectionRootWithSep = collectionRoot.endsWith(path10.sep) ? collectionRoot : `${collectionRoot}${path10.sep}`;
6408
+ const resolved = path11.resolve(collectionRoot, filename);
6409
+ const collectionRootWithSep = collectionRoot.endsWith(path11.sep) ? collectionRoot : `${collectionRoot}${path11.sep}`;
5817
6410
  if (!resolved.startsWith(collectionRootWithSep)) {
5818
6411
  throw new ContentStoreError("Path traversal detected", "VALIDATION");
5819
6412
  }
5820
6413
  return {
5821
6414
  absolutePath: resolved,
5822
- relativePath: path10.relative(this.root, resolved),
6415
+ relativePath: path11.relative(this.root, resolved),
5823
6416
  id,
5824
6417
  entryTypeName: finalEntryTypeName,
5825
6418
  existed
@@ -5855,8 +6448,8 @@ var init_generate_ai_content = __esm({
5855
6448
  async read(collectionPath, slug = "", options = {}) {
5856
6449
  const schemaItem = this.assertSchemaItem(collectionPath);
5857
6450
  const { absolutePath, relativePath, entryTypeName: resolvedEntryTypeName } = await this.buildPaths(schemaItem, slug);
5858
- const stat = await fs9.stat(absolutePath);
5859
- const raw = await fs9.readFile(absolutePath, "utf8");
6451
+ const stat = await fs10.stat(absolutePath);
6452
+ const raw = await fs10.readFile(absolutePath, "utf8");
5860
6453
  let doc;
5861
6454
  let format;
5862
6455
  let fields;
@@ -5895,7 +6488,7 @@ var init_generate_ai_content = __esm({
5895
6488
  absolutePath
5896
6489
  };
5897
6490
  } else {
5898
- const parsed = matter(raw);
6491
+ const parsed = matter2(raw);
5899
6492
  doc = {
5900
6493
  collection: schemaItem.logicalPath,
5901
6494
  collectionName: schemaItem.name,
@@ -5958,7 +6551,7 @@ var init_generate_ai_content = __esm({
5958
6551
  const indexedPath = guardIndex.findById(id)?.relativePath ?? null;
5959
6552
  if (indexedPath && indexedPath !== relativePath) {
5960
6553
  const quarantined = guardIndex.getDuplicateFor(id);
5961
- const bothOnDisk = await filePathExists3(absolutePath) && await filePathExists3(path10.join(this.root, indexedPath));
6554
+ const bothOnDisk = await filePathExists3(absolutePath) && await filePathExists3(path11.join(this.root, indexedPath));
5962
6555
  if (quarantined || bothOnDisk) {
5963
6556
  throw new DuplicateContentIdError(id, [
5964
6557
  indexedPath,
@@ -5969,10 +6562,18 @@ var init_generate_ai_content = __esm({
5969
6562
  }
5970
6563
  }
5971
6564
  }
5972
- await fs9.mkdir(path10.dirname(absolutePath), { recursive: true });
6565
+ if (!await filePathExists3(absolutePath)) {
6566
+ const chosenSlug = extractSlugFromFilename(path11.basename(absolutePath));
6567
+ const routable = parseSlug(chosenSlug);
6568
+ if (!routable.ok) {
6569
+ 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");
6570
+ }
6571
+ await this.assertUrlPathAvailable(path11.dirname(absolutePath), chosenSlug);
6572
+ }
6573
+ await fs10.mkdir(path11.dirname(absolutePath), { recursive: true });
5973
6574
  if (input.expectedVersion !== void 0) {
5974
6575
  try {
5975
- const existing = await fs9.stat(absolutePath);
6576
+ const existing = await fs10.stat(absolutePath);
5976
6577
  if (input.expectedVersion === null) {
5977
6578
  throw new ContentConflictError("An entry with this slug already exists");
5978
6579
  }
@@ -5989,7 +6590,7 @@ var init_generate_ai_content = __esm({
5989
6590
  }
5990
6591
  }
5991
6592
  if (existingId && !await filePathExists3(absolutePath)) {
5992
- const actualRelPaths = await this.findEntryPathsById(path10.dirname(absolutePath), existingId);
6593
+ const actualRelPaths = await this.findEntryPathsById(path11.dirname(absolutePath), existingId);
5993
6594
  if (actualRelPaths.length > 1) {
5994
6595
  throw new DuplicateContentIdError(existingId, actualRelPaths);
5995
6596
  }
@@ -5999,14 +6600,15 @@ var init_generate_ai_content = __esm({
5999
6600
  throw new ContentConflictError();
6000
6601
  }
6001
6602
  }
6603
+ const existingRaw = input.format === "json" ? void 0 : await readFileIfExists(absolutePath);
6002
6604
  let content;
6003
6605
  if (input.format === "json") {
6004
6606
  content = `${JSON.stringify(input.data ?? {}, null, 2)}
6005
6607
  `;
6006
6608
  } else if (input.format === "yaml") {
6007
- content = yamlStringify(input.data ?? {});
6609
+ content = serializeYaml(input.data ?? {}, existingRaw);
6008
6610
  } else {
6009
- content = matter.stringify(input.body, input.data ?? {});
6611
+ content = serializeFrontmatter(input.body, input.data ?? {}, existingRaw);
6010
6612
  }
6011
6613
  await atomicWriteFile2(absolutePath, content);
6012
6614
  const liveIndex = this._idIndex;
@@ -6015,7 +6617,7 @@ var init_generate_ai_content = __esm({
6015
6617
  const existing = liveIndex.findById(id);
6016
6618
  if (existing) {
6017
6619
  if (existing.relativePath !== relativePath) {
6018
- staleOldAbsPath = path10.join(this.root, existing.relativePath);
6620
+ staleOldAbsPath = path11.join(this.root, existing.relativePath);
6019
6621
  liveIndex.updatePath(existing.id, relativePath);
6020
6622
  }
6021
6623
  } else {
@@ -6028,13 +6630,13 @@ var init_generate_ai_content = __esm({
6028
6630
  }
6029
6631
  }
6030
6632
  if (staleOldAbsPath) {
6031
- await fs9.unlink(staleOldAbsPath).catch((err) => {
6633
+ await fs10.unlink(staleOldAbsPath).catch((err) => {
6032
6634
  if (!isNodeError3(err) || err.code !== "ENOENT")
6033
6635
  throw err;
6034
6636
  });
6035
6637
  }
6036
6638
  await this.recordOwnMutation(liveIndex);
6037
- const afterStat = await fs9.stat(absolutePath);
6639
+ const afterStat = await fs10.stat(absolutePath);
6038
6640
  const base = {
6039
6641
  collection: schemaItem.logicalPath,
6040
6642
  collectionName: schemaItem.name,
@@ -6112,7 +6714,7 @@ var init_generate_ai_content = __esm({
6112
6714
  const schemaItem = this.assertSchemaItem(collectionPath);
6113
6715
  const { absolutePath } = await this.buildPaths(schemaItem, slug);
6114
6716
  try {
6115
- await fs9.access(absolutePath);
6717
+ await fs10.access(absolutePath);
6116
6718
  return true;
6117
6719
  } catch (err) {
6118
6720
  if (isNodeError3(err) && err.code === "ENOENT")
@@ -6151,7 +6753,7 @@ var init_generate_ai_content = __esm({
6151
6753
  const collectionRoot = await resolveCollectionPath(this.root, collectionPath);
6152
6754
  if (!collectionRoot)
6153
6755
  return 0;
6154
- const entries = await fs9.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
6756
+ const entries = await fs10.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
6155
6757
  return entries.filter((entry) => !entry.isDirectory() && extractEntryTypeFromFilename(entry.name) === entryTypeName).length;
6156
6758
  }
6157
6759
  /**
@@ -6175,7 +6777,7 @@ var init_generate_ai_content = __esm({
6175
6777
  const collection = this.assertCollection(collectionPath);
6176
6778
  const prePass = await this.buildPaths(collection, slug);
6177
6779
  if (!prePass.existed) {
6178
- await fs9.unlink(prePass.absolutePath);
6780
+ await fs10.unlink(prePass.absolutePath);
6179
6781
  return;
6180
6782
  }
6181
6783
  let lockKey = this.entryLockKey(collection, slug, prePass);
@@ -6191,7 +6793,7 @@ var init_generate_ai_content = __esm({
6191
6793
  return RETRY_KEY;
6192
6794
  }
6193
6795
  const { absolutePath, relativePath } = inLock;
6194
- await fs9.unlink(absolutePath);
6796
+ await fs10.unlink(absolutePath);
6195
6797
  const liveIndex = this._idIndex;
6196
6798
  const id = liveIndex.findByPath(relativePath);
6197
6799
  if (id) {
@@ -6227,6 +6829,10 @@ var init_generate_ai_content = __esm({
6227
6829
  if (currentSlug === safeNewSlug) {
6228
6830
  return { newPath: `${collectionPath}/${currentSlug}` };
6229
6831
  }
6832
+ const routable = parseSlug(safeNewSlug);
6833
+ if (!routable.ok) {
6834
+ 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");
6835
+ }
6230
6836
  const prePass = await this.buildPaths(collection, currentSlug);
6231
6837
  if (!prePass.existed) {
6232
6838
  throw new ContentStoreError(`Entry not found: ${currentSlug}`, "NOT_FOUND");
@@ -6253,7 +6859,7 @@ var init_generate_ai_content = __esm({
6253
6859
  }
6254
6860
  destLockKey = currentDestKey;
6255
6861
  const { absolutePath: currentPath, relativePath: currentRelPath } = inLock;
6256
- const currentFilename = path10.basename(currentPath);
6862
+ const currentFilename = path11.basename(currentPath);
6257
6863
  const parts = currentFilename.split(".");
6258
6864
  if (parts.length < 4) {
6259
6865
  throw new ContentStoreError(`Invalid entry filename format: ${currentFilename}`, "VALIDATION");
@@ -6262,10 +6868,10 @@ var init_generate_ai_content = __esm({
6262
6868
  const contentId = parts[parts.length - 2];
6263
6869
  const ext = `.${parts[parts.length - 1]}`;
6264
6870
  const newFilename = `${entryTypeName}.${safeNewSlug}.${contentId}${ext}`;
6265
- const parentDir = path10.dirname(currentPath);
6266
- const newPath = path10.join(parentDir, newFilename);
6871
+ const parentDir = path11.dirname(currentPath);
6872
+ const newPath = path11.join(parentDir, newFilename);
6267
6873
  try {
6268
- const entries = await fs9.readdir(parentDir, { withFileTypes: true });
6874
+ const entries = await fs10.readdir(parentDir, { withFileTypes: true });
6269
6875
  for (const entry of entries) {
6270
6876
  if (entry.isDirectory())
6271
6877
  continue;
@@ -6278,16 +6884,17 @@ var init_generate_ai_content = __esm({
6278
6884
  if (err instanceof ContentStoreError)
6279
6885
  throw err;
6280
6886
  }
6887
+ await this.assertUrlPathAvailable(parentDir, safeNewSlug);
6281
6888
  try {
6282
- await fs9.link(currentPath, newPath);
6889
+ await fs10.link(currentPath, newPath);
6283
6890
  } catch (err) {
6284
6891
  if (isNodeError3(err) && err.code === "EEXIST") {
6285
6892
  throw new ContentStoreError(`Entry with slug "${safeNewSlug}" already exists in collection "${collectionPath}"`, "VALIDATION");
6286
6893
  }
6287
6894
  throw err;
6288
6895
  }
6289
- await fs9.unlink(currentPath);
6290
- const newRelativePath = path10.relative(this.root, newPath);
6896
+ await fs10.unlink(currentPath);
6897
+ const newRelativePath = path11.relative(this.root, newPath);
6291
6898
  const liveIndex = this._idIndex;
6292
6899
  const entryId = liveIndex.findByPath(currentRelPath);
6293
6900
  if (entryId) {
@@ -6359,34 +6966,57 @@ var init_generate_ai_content = __esm({
6359
6966
  }
6360
6967
  return entries;
6361
6968
  }
6969
+ /**
6970
+ * Resolve every `reference` field in `data` against `fields`, returning a copy.
6971
+ *
6972
+ * This is what `read()` applies automatically (unless `resolveReferences: false`), exposed
6973
+ * so BATCH surfaces can opt into the same resolution without duplicating the walk: the
6974
+ * listing primitives in content-listing.ts and content-tree.ts read entry files straight
6975
+ * off disk and never touch this store, so before this existed a `reference` field reached
6976
+ * `listEntries()`/`buildContentTree()` callers as a bare id string or `null`.
6977
+ *
6978
+ * Pass one {@link ReferenceResolveCache} across a whole batch so a target referenced by
6979
+ * many entries is read once rather than once per referencing entry. Omit `cache` and the
6980
+ * behavior is exactly what `read()` has always done, unmemoized.
6981
+ *
6982
+ * **Path ACLs are not consulted for the targets.** Resolution goes through this store's
6983
+ * own `read()`, below the per-entry permission check in content-reader.ts — so a resolved
6984
+ * target may be an entry the caller could not `read()` directly. That is pre-existing
6985
+ * `read()` behavior, matched here on purpose so one rule covers both; see the
6986
+ * `resolveReferences` option in content-listing.ts for the note adopters see.
6987
+ */
6988
+ async resolveReferences(data, fields, cache) {
6989
+ return this.resolveReferencesInData(data, fields, cache);
6990
+ }
6362
6991
  /**
6363
6992
  * Recursively resolve reference fields in data.
6364
6993
  * This traverses objects, arrays, and blocks to find and resolve all reference fields.
6365
6994
  */
6366
- async resolveReferencesInData(data, fields) {
6995
+ async resolveReferencesInData(data, fields, cache) {
6367
6996
  const resolved = { ...data };
6368
6997
  const idIndex = await this.idIndex();
6369
6998
  for (const field of fields) {
6370
6999
  if (field.type === "group") {
6371
- const groupResolved = await this.resolveReferencesInData(resolved, field.fields);
7000
+ const groupResolved = await this.resolveReferencesInData(resolved, field.fields, cache);
6372
7001
  Object.assign(resolved, groupResolved);
6373
7002
  continue;
6374
7003
  }
6375
7004
  const value = data[field.name];
6376
7005
  if (field.type === "reference") {
7006
+ const includeBody = field.includeBody === true;
6377
7007
  if (typeof value === "string" && value) {
6378
- resolved[field.name] = await this.resolveSingleReference(value, idIndex);
7008
+ resolved[field.name] = await this.resolveSingleReference(value, idIndex, includeBody, cache);
6379
7009
  } else if (field.list && Array.isArray(value)) {
6380
- resolved[field.name] = await Promise.all(value.map((id) => typeof id === "string" ? this.resolveSingleReference(id, idIndex) : null));
7010
+ resolved[field.name] = await Promise.all(value.map((id) => typeof id === "string" ? this.resolveSingleReference(id, idIndex, includeBody, cache) : null));
6381
7011
  }
6382
7012
  } else if (field.type === "object" && value) {
6383
7013
  const objectField = field;
6384
7014
  if (!objectField.fields)
6385
7015
  continue;
6386
7016
  if (objectField.list && Array.isArray(value)) {
6387
- resolved[field.name] = await Promise.all(value.map((item) => typeof item === "object" && item !== null ? this.resolveReferencesInData(item, objectField.fields) : item));
7017
+ resolved[field.name] = await Promise.all(value.map((item) => typeof item === "object" && item !== null ? this.resolveReferencesInData(item, objectField.fields, cache) : item));
6388
7018
  } else if (typeof value === "object") {
6389
- resolved[field.name] = await this.resolveReferencesInData(value, objectField.fields);
7019
+ resolved[field.name] = await this.resolveReferencesInData(value, objectField.fields, cache);
6390
7020
  }
6391
7021
  } else if (field.type === "block" && Array.isArray(value)) {
6392
7022
  const blockField = field;
@@ -6399,7 +7029,7 @@ var init_generate_ai_content = __esm({
6399
7029
  return block;
6400
7030
  return {
6401
7031
  ...b,
6402
- value: await this.resolveReferencesInData(b.value, template.fields)
7032
+ value: await this.resolveReferencesInData(b.value, template.fields, cache)
6403
7033
  };
6404
7034
  }));
6405
7035
  }
@@ -6407,7 +7037,56 @@ var init_generate_ai_content = __esm({
6407
7037
  return resolved;
6408
7038
  }
6409
7039
  /**
6410
- * Resolve a single reference ID to full entry data.
7040
+ * Resolve a single reference ID to full entry data, memoized when a batch cache is
7041
+ * supplied. Without a cache this is a straight pass-through to the uncached path, so
7042
+ * `read()` behaves exactly as it always has.
7043
+ *
7044
+ * Every occurrence gets its OWN deep copy, even on a cache hit. Without that, the memo
7045
+ * would hand one shared object to all 40 entries referencing the same block — a mutation
7046
+ * in one caller's `extract` (truncating a body for a search index, deleting a field) would
7047
+ * silently rewrite it for every sibling, and both `list: true` elements of `[id, id]` would
7048
+ * be the same instance. The copy is what keeps the cache a pure performance optimization
7049
+ * instead of a semantic change.
7050
+ *
7051
+ * Note the uncached path is NOT the clean baseline it looks like. For a json/yaml target it
7052
+ * genuinely reparses fresh per occurrence, but for md/mdx gray-matter serves `data` from a
7053
+ * process-global content-keyed cache, so `resolveSingleReferenceOnce`'s top-level spread
7054
+ * severs exactly one level and NESTED frontmatter objects alias across occurrences, calls and
7055
+ * requests. That is pre-existing `read()` behavior and out of scope here — deliberately, since
7056
+ * touching it would change `read()` — but it means the cached path is the safer of the two,
7057
+ * not a relaxation of a guarantee the uncached one provides. See
7058
+ * `.claude/future-tasks/graymatter-cache-shared-frontmatter.md`.
7059
+ *
7060
+ * What the copy costs, measured rather than assumed (2000 occurrences of one target, local
7061
+ * disk, so every read hits the page cache — the friendliest possible case for NOT caching):
7062
+ * a snippet-sized target is ~63x cheaper to clone than to re-read-and-parse, while a 265KB
7063
+ * JSON target is ~0.8x, i.e. cloning is marginally SLOWER than reparsing it. So the win is
7064
+ * large in the case this exists for and roughly a wash at the pathological end, never a
7065
+ * blow-up. Two things keep the bad end narrow: an md/mdx target resolves to its FRONTMATTER
7066
+ * only *unless the field sets `includeBody`* (`read()` puts the body on `doc.body`, which
7067
+ * `resolveSingleReferenceOnce` spreads in only for an embedding field), so by default body
7068
+ * size is irrelevant no matter how long the document and only a genuinely huge JSON/YAML
7069
+ * target reaches the wash. **`includeBody: true` is the case that CAN reach it on markdown**:
7070
+ * the body then sits inside the memoized object and is cloned once per referencing entry, so
7071
+ * a long document embedded by many pages pays that repeatedly — the reason `includeBody`
7072
+ * exists as an opt-in per field rather than as resolution's default. And in the deployment this
7073
+ * targets, content lives on EFS/NFS where the syscall the memo removes dominates parse and
7074
+ * clone alike, which the local-disk numbers above understate badly. Correctness is the
7075
+ * reason for the copy regardless; the numbers are here so nobody has to re-derive them
7076
+ * before touching this.
7077
+ */
7078
+ resolveSingleReference(id, idIndex, includeBody, cache) {
7079
+ if (!cache)
7080
+ return this.resolveSingleReferenceUncached(id, idIndex, includeBody);
7081
+ const key = includeBody ? `${id}:body` : id;
7082
+ let pending = cache.get(key);
7083
+ if (!pending) {
7084
+ pending = this.resolveSingleReferenceUncached(id, idIndex, includeBody);
7085
+ cache.set(key, pending);
7086
+ }
7087
+ return pending.then((resolved) => resolved === null ? null : structuredClone(resolved));
7088
+ }
7089
+ /**
6411
7090
  * Returns null if the reference is invalid or missing.
6412
7091
  * Includes id, slug, and collection fields for debugging.
6413
7092
  *
@@ -6421,16 +7100,21 @@ var init_generate_ai_content = __esm({
6421
7100
  * idIndex() dedupes concurrent builds — so a sibling call that wins the
6422
7101
  * throttle and rebuilds heals every other miss in the same batch, not just
6423
7102
  * the first.
7103
+ *
7104
+ * A {@link ReferenceResolveCache} sits ABOVE this, never inside it, so every
7105
+ * distinct id still runs the full refresh-and-retry above. What a cache changes
7106
+ * is only that repeats of the SAME id in one batch share that one attempt's
7107
+ * outcome instead of each getting an independent throw of the dice.
6424
7108
  */
6425
- async resolveSingleReference(id, idIndex) {
6426
- const first = await this.resolveSingleReferenceOnce(id, idIndex);
7109
+ async resolveSingleReferenceUncached(id, idIndex, includeBody) {
7110
+ const first = await this.resolveSingleReferenceOnce(id, idIndex, includeBody);
6427
7111
  if (first !== STALE_LOOKUP)
6428
7112
  return first;
6429
7113
  await this.refreshIndexForSuspiciousLookup();
6430
- const second = await this.resolveSingleReferenceOnce(id, await this.idIndex());
7114
+ const second = await this.resolveSingleReferenceOnce(id, await this.idIndex(), includeBody);
6431
7115
  return second === STALE_LOOKUP ? null : second;
6432
7116
  }
6433
- async resolveSingleReferenceOnce(id, idIndex) {
7117
+ async resolveSingleReferenceOnce(id, idIndex, includeBody) {
6434
7118
  try {
6435
7119
  const location = idIndex.findById(id);
6436
7120
  if (!location)
@@ -6441,12 +7125,13 @@ var init_generate_ai_content = __esm({
6441
7125
  const doc = await this.read(location.collection, location.slug, {
6442
7126
  resolveReferences: false
6443
7127
  });
6444
- return {
7128
+ const bodyForEmbed = includeBody && "body" in doc ? { fieldName: doc.bodyFieldName, value: doc.body } : void 0;
7129
+ return buildResolvedReference(doc.data, {
6445
7130
  id,
6446
7131
  slug: location.slug,
6447
7132
  collection: location.collection,
6448
- ...doc.data
6449
- };
7133
+ urlPath: computeEntryUrl(location.collection, location.slug, this.contentRootName)
7134
+ }, bodyForEmbed);
6450
7135
  } catch (error) {
6451
7136
  if (isNodeError3(error) && error.code === "ENOENT")
6452
7137
  return STALE_LOOKUP;
@@ -6471,7 +7156,6 @@ var init_generate_ai_content = __esm({
6471
7156
  "reference",
6472
7157
  "image"
6473
7158
  ]);
6474
- init_normalize2();
6475
7159
  log22 = createDebugLogger2({ prefix: "EntryLinks" });
6476
7160
  BASE58_CHAR = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]";
6477
7161
  ENTRY_LINK_PATTERN = new RegExp(`entry:(${BASE58_CHAR}{12})(#[^\\s)>"']*)?`, "g");
@@ -6487,10 +7171,10 @@ var init_generate_ai_content = __esm({
6487
7171
  constructor(root) {
6488
7172
  this.regenInFlight = null;
6489
7173
  this.lastForcedRefreshMs = 0;
6490
- this.root = path12.resolve(root);
6491
- this.registryPath = path12.join(this.root, REGISTRY_FILE);
6492
- this.stalePath = path12.join(this.root, REGISTRY_STALE_FILE);
6493
- this.tempPath = path12.join(this.root, REGISTRY_TEMP_FILE);
7174
+ this.root = path13.resolve(root);
7175
+ this.registryPath = path13.join(this.root, REGISTRY_FILE);
7176
+ this.stalePath = path13.join(this.root, REGISTRY_STALE_FILE);
7177
+ this.tempPath = path13.join(this.root, REGISTRY_TEMP_FILE);
6494
7178
  }
6495
7179
  /**
6496
7180
  * Returns all branches. Uses the cached snapshot if its embedded generation
@@ -6499,7 +7183,7 @@ var init_generate_ai_content = __esm({
6499
7183
  async list() {
6500
7184
  let parsed;
6501
7185
  try {
6502
- const raw = await fs11.readFile(this.registryPath, "utf8");
7186
+ const raw = await fs12.readFile(this.registryPath, "utf8");
6503
7187
  parsed = JSON.parse(raw);
6504
7188
  } catch (err) {
6505
7189
  if (isNotFoundError3(err)) {
@@ -6588,7 +7272,7 @@ var init_generate_ai_content = __esm({
6588
7272
  async performRegenerate() {
6589
7273
  const read = await readResourceGeneration(this.root, RESOURCE2);
6590
7274
  const branches = await this.scanBranchDirectories();
6591
- await fs11.unlink(this.stalePath).catch(() => {
7275
+ await fs12.unlink(this.stalePath).catch(() => {
6592
7276
  });
6593
7277
  if (!read.ok) {
6594
7278
  return branches;
@@ -6599,12 +7283,12 @@ var init_generate_ai_content = __esm({
6599
7283
  generation: read.token
6600
7284
  };
6601
7285
  const uniqueTempPath = `${this.tempPath}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
6602
- await fs11.mkdir(this.root, { recursive: true });
6603
- await fs11.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
7286
+ await fs12.mkdir(this.root, { recursive: true });
7287
+ await fs12.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
6604
7288
  try {
6605
- await fs11.rename(uniqueTempPath, this.registryPath);
7289
+ await fs12.rename(uniqueTempPath, this.registryPath);
6606
7290
  } catch (err) {
6607
- await fs11.unlink(uniqueTempPath).catch(() => {
7291
+ await fs12.unlink(uniqueTempPath).catch(() => {
6608
7292
  });
6609
7293
  throw err;
6610
7294
  }
@@ -6618,12 +7302,12 @@ var init_generate_ai_content = __esm({
6618
7302
  async scanBranchDirectories() {
6619
7303
  const branches = [];
6620
7304
  try {
6621
- const entries = await fs11.readdir(this.root, { withFileTypes: true });
7305
+ const entries = await fs12.readdir(this.root, { withFileTypes: true });
6622
7306
  for (const entry of entries) {
6623
7307
  if (!entry.isDirectory() || entry.name.startsWith(".")) {
6624
7308
  continue;
6625
7309
  }
6626
- const branchRoot = path12.join(this.root, entry.name);
7310
+ const branchRoot = path13.join(this.root, entry.name);
6627
7311
  let meta;
6628
7312
  try {
6629
7313
  meta = await BranchMetadataFileManager.loadOnly(branchRoot);
@@ -6674,8 +7358,8 @@ var init_generate_ai_content = __esm({
6674
7358
  };
6675
7359
  BranchMetadataFileManager = class _BranchMetadataFileManager {
6676
7360
  constructor(branchRoot, baseRoot, options) {
6677
- this.branchRoot = path14.resolve(branchRoot);
6678
- this.filePath = path14.join(this.branchRoot, BRANCH_META_DIR, BRANCH_META_FILE);
7361
+ this.branchRoot = path15.resolve(branchRoot);
7362
+ this.filePath = path15.join(this.branchRoot, BRANCH_META_DIR, BRANCH_META_FILE);
6679
7363
  this.baseRoot = baseRoot;
6680
7364
  this.settleMs = options?.settleMs;
6681
7365
  }
@@ -6684,10 +7368,10 @@ var init_generate_ai_content = __esm({
6684
7368
  * Use this for read-only access (e.g., in registry scanning or loadBranchContext).
6685
7369
  */
6686
7370
  static async loadOnly(branchRoot) {
6687
- const resolvedRoot = path14.resolve(branchRoot);
6688
- const filePath = path14.join(resolvedRoot, BRANCH_META_DIR, BRANCH_META_FILE);
7371
+ const resolvedRoot = path15.resolve(branchRoot);
7372
+ const filePath = path15.join(resolvedRoot, BRANCH_META_DIR, BRANCH_META_FILE);
6689
7373
  try {
6690
- const raw = await fs13.readFile(filePath, "utf8");
7374
+ const raw = await fs14.readFile(filePath, "utf8");
6691
7375
  return JSON.parse(raw);
6692
7376
  } catch (err) {
6693
7377
  if (isNotFoundError3(err)) {
@@ -6708,7 +7392,7 @@ var init_generate_ai_content = __esm({
6708
7392
  }
6709
7393
  async load() {
6710
7394
  try {
6711
- const raw = await fs13.readFile(this.filePath, "utf8");
7395
+ const raw = await fs14.readFile(this.filePath, "utf8");
6712
7396
  const parsed = JSON.parse(raw);
6713
7397
  const version = parsed.version ?? 0;
6714
7398
  return { meta: parsed, version };
@@ -6770,7 +7454,7 @@ var init_generate_ai_content = __esm({
6770
7454
  */
6771
7455
  async save(incoming) {
6772
7456
  try {
6773
- await fs13.stat(this.branchRoot);
7457
+ await fs14.stat(this.branchRoot);
6774
7458
  } catch (err) {
6775
7459
  if (isNotFoundError3(err)) {
6776
7460
  throw new BranchMetadataConflictError("Branch no longer exists");
@@ -6883,12 +7567,12 @@ var init_generate_ai_content = __esm({
6883
7567
  };
6884
7568
  GitManager = class _GitManager {
6885
7569
  constructor(options, gitOptions) {
6886
- this.repoPath = path15.resolve(options.repoPath);
7570
+ this.repoPath = path16.resolve(options.repoPath);
6887
7571
  this.baseBranch = options.baseBranch ?? "main";
6888
7572
  this.remote = options.remote ?? "origin";
6889
7573
  this.skipIndexMarker = options.skipIndexMarker ?? false;
6890
7574
  this.git = simpleGit2({ baseDir: this.repoPath, ...gitOptions });
6891
- this.git.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path15.dirname(this.repoPath) }));
7575
+ this.git.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path16.dirname(this.repoPath) }));
6892
7576
  }
6893
7577
  static async cloneRepo(remoteUrl, targetPath, baseBranch = "main") {
6894
7578
  log5.debug("git", "Cloning repository", {
@@ -6930,11 +7614,11 @@ var init_generate_ai_content = __esm({
6930
7614
  remotePath: options.remotePath,
6931
7615
  baseBranch: options.baseBranch
6932
7616
  });
6933
- const remoteParent = path15.dirname(options.remotePath);
7617
+ const remoteParent = path16.dirname(options.remotePath);
6934
7618
  releaseLock = await acquireProvisioningLock(remoteParent, ".remote-init.lock");
6935
7619
  let remoteExists = false;
6936
7620
  try {
6937
- const stat = await fs14.stat(options.remotePath);
7621
+ const stat = await fs15.stat(options.remotePath);
6938
7622
  remoteExists = stat.isDirectory();
6939
7623
  } catch (err) {
6940
7624
  if (!isNotFoundError3(err))
@@ -7186,7 +7870,7 @@ var init_generate_ai_content = __esm({
7186
7870
  */
7187
7871
  static async validateGitRepoExists(repoPath) {
7188
7872
  try {
7189
- const stat = await fs14.stat(path15.join(repoPath, ".git"));
7873
+ const stat = await fs15.stat(path16.join(repoPath, ".git"));
7190
7874
  if (!stat.isDirectory()) {
7191
7875
  throw new Error(`Expected git repo at ${repoPath}`);
7192
7876
  }
@@ -7264,7 +7948,7 @@ var init_generate_ai_content = __esm({
7264
7948
  }
7265
7949
  if (config.autoDetectRemotePath) {
7266
7950
  try {
7267
- const stat = await fs14.stat(config.autoDetectRemotePath);
7951
+ const stat = await fs15.stat(config.autoDetectRemotePath);
7268
7952
  if (stat.isDirectory()) {
7269
7953
  log5.debug("git", "Auto-detected local remote", {
7270
7954
  path: config.autoDetectRemotePath
@@ -7277,8 +7961,8 @@ var init_generate_ai_content = __esm({
7277
7961
  if (config.shouldAutoInitLocal) {
7278
7962
  const gitRoot = await this.findGitRoot();
7279
7963
  const sourceRoot = options.sourceRoot;
7280
- const sourcePath = sourceRoot ? path15.resolve(gitRoot, sourceRoot) : gitRoot;
7281
- const localRemotePath = path15.join(sourcePath, config.defaultRemotePath);
7964
+ const sourcePath = sourceRoot ? path16.resolve(gitRoot, sourceRoot) : gitRoot;
7965
+ const localRemotePath = path16.join(sourcePath, config.defaultRemotePath);
7282
7966
  await this.ensureLocalSimulatedRemote({
7283
7967
  remotePath: localRemotePath,
7284
7968
  sourcePath: gitRoot,
@@ -7306,7 +7990,7 @@ var init_generate_ai_content = __esm({
7306
7990
  static async repoExistsAt(workspacePath) {
7307
7991
  try {
7308
7992
  const checkGit = simpleGit2({ baseDir: workspacePath });
7309
- checkGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path15.dirname(workspacePath) }));
7993
+ checkGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path16.dirname(workspacePath) }));
7310
7994
  await checkGit.raw(["rev-parse", "--git-dir"]);
7311
7995
  return true;
7312
7996
  } catch {
@@ -7328,19 +8012,19 @@ var init_generate_ai_content = __esm({
7328
8012
  const baseBranch = await resolveBaseBranch({
7329
8013
  defaultBaseBranch: options.baseBranch,
7330
8014
  mode: options.mode,
7331
- detectFrom: options.sourceRoot ? path15.resolve(process.cwd(), options.sourceRoot) : process.cwd()
8015
+ detectFrom: options.sourceRoot ? path16.resolve(process.cwd(), options.sourceRoot) : process.cwd()
7332
8016
  });
7333
8017
  const remoteName = options.remoteName ?? "origin";
7334
8018
  const repoExists = await _GitManager.repoExistsAt(options.workspacePath);
7335
8019
  if (!repoExists) {
7336
- const gitPath = path15.join(options.workspacePath, ".git");
8020
+ const gitPath = path16.join(options.workspacePath, ".git");
7337
8021
  try {
7338
- const stat = await fs14.stat(gitPath);
8022
+ const stat = await fs15.stat(gitPath);
7339
8023
  if (stat.isDirectory()) {
7340
8024
  log5.debug("git", "Removing corrupt .git directory", {
7341
8025
  workspacePath: options.workspacePath
7342
8026
  });
7343
- await fs14.rm(gitPath, { recursive: true });
8027
+ await fs15.rm(gitPath, { recursive: true });
7344
8028
  }
7345
8029
  } catch (cleanupErr) {
7346
8030
  if (!isNotFoundError3(cleanupErr))
@@ -7367,7 +8051,7 @@ var init_generate_ai_content = __esm({
7367
8051
  }
7368
8052
  justCloned = true;
7369
8053
  const freshGit = simpleGit2({ baseDir: options.workspacePath });
7370
- freshGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path15.dirname(options.workspacePath) }));
8054
+ freshGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path16.dirname(options.workspacePath) }));
7371
8055
  await freshGit.addConfig("canopycms.managed", "true");
7372
8056
  await freshGit.addConfig("user.name", options.gitBotAuthorName);
7373
8057
  await freshGit.addConfig("user.email", options.gitBotAuthorEmail);
@@ -7718,9 +8402,9 @@ var init_generate_ai_content = __esm({
7718
8402
  } catch {
7719
8403
  }
7720
8404
  for (const [filePath, content] of Object.entries(initialFiles)) {
7721
- const absolutePath = path15.join(this.repoPath, filePath);
7722
- await fs14.mkdir(path15.dirname(absolutePath), { recursive: true });
7723
- await fs14.writeFile(absolutePath, content, "utf-8");
8405
+ const absolutePath = path16.join(this.repoPath, filePath);
8406
+ await fs15.mkdir(path16.dirname(absolutePath), { recursive: true });
8407
+ await fs15.writeFile(absolutePath, content, "utf-8");
7724
8408
  await this.git.add(filePath);
7725
8409
  }
7726
8410
  await this.git.commit("Initialize settings branch", ["--allow-empty"]);
@@ -7747,7 +8431,7 @@ var init_generate_ai_content = __esm({
7747
8431
  branchName: options.branchName,
7748
8432
  mode: options.mode
7749
8433
  });
7750
- releaseLock = await acquireProvisioningLock(path16.dirname(options.branchRoot), `.${path16.basename(options.branchRoot)}.init.lock`);
8434
+ releaseLock = await acquireProvisioningLock(path17.dirname(options.branchRoot), `.${path17.basename(options.branchRoot)}.init.lock`);
7751
8435
  await GitManager.initializeWorkspace({
7752
8436
  workspacePath: options.branchRoot,
7753
8437
  branchName: options.branchName,
@@ -7788,7 +8472,7 @@ var init_generate_ai_content = __esm({
7788
8472
  const baseBranch = await resolveBaseBranch({
7789
8473
  defaultBaseBranch: this.config.defaultBaseBranch,
7790
8474
  mode,
7791
- detectFrom: this.config.sourceRoot ? path16.resolve(process.cwd(), this.config.sourceRoot) : void 0
8475
+ detectFrom: this.config.sourceRoot ? path17.resolve(process.cwd(), this.config.sourceRoot) : void 0
7792
8476
  });
7793
8477
  await this.ensureGitWorkspace({
7794
8478
  branchRoot,
@@ -7825,15 +8509,15 @@ var init_generate_ai_content = __esm({
7825
8509
  };
7826
8510
  readEntryData = async (filePath, format, bodyFieldName = "body") => {
7827
8511
  try {
7828
- const raw = await fs15.readFile(filePath, "utf8");
8512
+ const raw = await fs16.readFile(filePath, "utf8");
7829
8513
  if (format === "json") {
7830
8514
  return asRecord(JSON.parse(raw));
7831
8515
  }
7832
8516
  if (format === "yaml") {
7833
8517
  return asRecord(yamlParse2(raw));
7834
8518
  }
7835
- const parsed = matter2(raw);
7836
- const data = parsed.data ?? {};
8519
+ const parsed = matter3(raw);
8520
+ const data = { ...parsed.data ?? {} };
7837
8521
  if (parsed.content) {
7838
8522
  data[bodyFieldName] = parsed.content;
7839
8523
  }
@@ -7845,30 +8529,28 @@ var init_generate_ai_content = __esm({
7845
8529
  return {};
7846
8530
  }
7847
8531
  };
7848
- parseTypedFilename = (filename, entryTypes) => {
7849
- const lastDot = filename.lastIndexOf(".");
7850
- if (lastDot === -1)
7851
- return null;
7852
- const nameWithoutExt = filename.slice(0, lastDot);
7853
- const parts = nameWithoutExt.split(".");
7854
- if (parts.length >= 3) {
7855
- const potentialType = parts[0];
7856
- const matchingType = entryTypes.find((e) => e.name === potentialType);
7857
- if (matchingType) {
7858
- const id = parts[parts.length - 1];
7859
- if (!isValidId(id))
7860
- return null;
7861
- const slug = parts.slice(1, -1).join(".").toLowerCase();
7862
- return {
7863
- type: potentialType,
7864
- slug,
7865
- id
7866
- };
7867
- }
7868
- }
7869
- return null;
8532
+ createReferenceResolver = (branchRoot, flatSchema, contentRootName) => ({
8533
+ store: new ContentStore(branchRoot, flatSchema, { contentRootName }),
8534
+ cache: createReferenceResolveCache()
8535
+ });
8536
+ resolveCollectionItemReferences = async (items, collection, resolver) => Promise.all(items.map(async (item) => {
8537
+ const fields = collection.entries?.find((e) => e.name === item.entryType)?.schema;
8538
+ if (!fields)
8539
+ return item;
8540
+ return {
8541
+ ...item,
8542
+ data: await resolver.store.resolveReferences(item.data, fields, resolver.cache)
8543
+ };
8544
+ }));
8545
+ looksLikeMalformedEntry = (filename, entryTypes) => {
8546
+ if (filename.startsWith(".") || filename.startsWith("_"))
8547
+ return false;
8548
+ const segments = filename.split(".");
8549
+ if (segments.length >= 4)
8550
+ return true;
8551
+ return segments.length === 3 && entryTypes.some((e) => e.name === segments[0]);
7870
8552
  };
7871
- listCollectionEntries = async (root, collection) => {
8553
+ listCollectionEntries = async (root, collection, onSkip) => {
7872
8554
  if (collection.type !== "collection" || !collection.entries) {
7873
8555
  return [];
7874
8556
  }
@@ -7888,7 +8570,7 @@ var init_generate_ai_content = __esm({
7888
8570
  normalizePath(root, collectionRoot);
7889
8571
  let dirents;
7890
8572
  try {
7891
- dirents = await fs15.readdir(collectionRoot, { withFileTypes: true });
8573
+ dirents = await fs16.readdir(collectionRoot, { withFileTypes: true });
7892
8574
  } catch (err) {
7893
8575
  if (isNotFoundError3(err))
7894
8576
  return [];
@@ -7896,11 +8578,14 @@ var init_generate_ai_content = __esm({
7896
8578
  }
7897
8579
  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));
7898
8580
  const entries = await Promise.all(files.map(async (file) => {
7899
- const absolutePath = path17.join(collectionRoot, file.name);
8581
+ const absolutePath = path18.join(collectionRoot, file.name);
7900
8582
  const relativePath = normalizePath(root, absolutePath);
7901
8583
  const parsed = parseTypedFilename(file.name, entryTypes);
7902
8584
  if (!parsed) {
7903
8585
  log7.warn("listCollectionEntries", `Skipping file with unrecognized filename format: ${file.name} (expected {type}.{slug}.{id}.{ext} with a known entry type and valid 12-char Base58 ID)`);
8586
+ if (looksLikeMalformedEntry(file.name, entryTypes)) {
8587
+ onSkip?.({ filename: file.name, collectionPath: collection.logicalPath });
8588
+ }
7904
8589
  return null;
7905
8590
  }
7906
8591
  const { type: entryTypeName, slug, id: contentId } = parsed;
@@ -7908,7 +8593,7 @@ var init_generate_ai_content = __esm({
7908
8593
  const format = entryType?.format || "json";
7909
8594
  const bodyField = entryType?.schema ? findBodyFieldName(entryType.schema) : "body";
7910
8595
  const [stats, data] = await Promise.all([
7911
- fs15.stat(absolutePath),
8596
+ fs16.stat(absolutePath),
7912
8597
  readEntryData(absolutePath, format, bodyField)
7913
8598
  ]);
7914
8599
  const item = {
@@ -7932,22 +8617,23 @@ var init_generate_ai_content = __esm({
7932
8617
  KNOWN_FIELD_TYPES = new Set(fieldTypes2);
7933
8618
  STRING_FIELD_TYPES = /* @__PURE__ */ new Set(["string", "markdown", "mdx", "code"]);
7934
8619
  selectOptionValues = (field) => (field.options ?? []).map((opt) => typeof opt === "string" ? opt : opt.value);
8620
+ GENERATED_RECORD_FILENAME = ".canopy-generated.json";
7935
8621
  jiti = createJiti2(import.meta.url);
7936
8622
  }
7937
8623
  });
7938
8624
 
7939
8625
  // dist/utils/atomic-write.js
7940
- import fs17 from "node:fs/promises";
7941
- import path20 from "node:path";
8626
+ import fs18 from "node:fs/promises";
8627
+ import path21 from "node:path";
7942
8628
  async function atomicWriteFile3(filePath, content) {
7943
- const dir = path20.dirname(filePath);
7944
- await fs17.mkdir(dir, { recursive: true });
8629
+ const dir = path21.dirname(filePath);
8630
+ await fs18.mkdir(dir, { recursive: true });
7945
8631
  const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
7946
- await fs17.writeFile(tempPath, content, typeof content === "string" ? "utf-8" : void 0);
8632
+ await fs18.writeFile(tempPath, content, typeof content === "string" ? "utf-8" : void 0);
7947
8633
  try {
7948
- await fs17.rename(tempPath, filePath);
8634
+ await fs18.rename(tempPath, filePath);
7949
8635
  } catch (err) {
7950
- await fs17.unlink(tempPath).catch(() => {
8636
+ await fs18.unlink(tempPath).catch(() => {
7951
8637
  });
7952
8638
  throw err;
7953
8639
  }
@@ -8067,11 +8753,11 @@ var init_debug2 = __esm({
8067
8753
  });
8068
8754
 
8069
8755
  // dist/resource-generation.js
8070
- import fs18 from "node:fs/promises";
8071
- import path21 from "node:path";
8756
+ import fs19 from "node:fs/promises";
8757
+ import path23 from "node:path";
8072
8758
  import { randomUUID as randomUUID3 } from "node:crypto";
8073
8759
  function resourceGenerationPath2(root, resource) {
8074
- return path21.resolve(root, META_DIR3, `${resource}.generation`);
8760
+ return path23.resolve(root, META_DIR3, `${resource}.generation`);
8075
8761
  }
8076
8762
  async function bumpResourceGeneration2(root, resource, options) {
8077
8763
  const token = randomUUID3();
@@ -8089,7 +8775,7 @@ async function bumpResourceGeneration2(root, resource, options) {
8089
8775
  }
8090
8776
  async function readResourceGeneration2(root, resource) {
8091
8777
  try {
8092
- const token = await fs18.readFile(resourceGenerationPath2(root, resource), "utf-8");
8778
+ const token = await fs19.readFile(resourceGenerationPath2(root, resource), "utf-8");
8093
8779
  return { ok: true, token };
8094
8780
  } catch (err) {
8095
8781
  if (isNodeError(err) && err.code === "ENOENT")
@@ -8116,10 +8802,10 @@ var init_resource_generation = __esm({
8116
8802
  });
8117
8803
 
8118
8804
  // dist/content-index-registry.js
8119
- import path23 from "node:path";
8805
+ import path24 from "node:path";
8120
8806
  function invalidateContentIndexesForRoot2(root) {
8121
- const rootKey = path23.resolve(root);
8122
- const prefix = rootKey + path23.sep;
8807
+ const rootKey = path24.resolve(root);
8808
+ const prefix = rootKey + path24.sep;
8123
8809
  for (const [storeRoot, refs] of registry2) {
8124
8810
  if (storeRoot !== rootKey && !storeRoot.startsWith(prefix))
8125
8811
  continue;
@@ -8177,7 +8863,7 @@ var init_normalize3 = __esm({
8177
8863
  function isValidContentId2(id) {
8178
8864
  return CONTENT_ID_PATTERN2.test(id);
8179
8865
  }
8180
- function parseSlug(slug) {
8866
+ function parseSlug2(slug) {
8181
8867
  if (!slug) {
8182
8868
  return {
8183
8869
  ok: false,
@@ -8287,7 +8973,7 @@ var init_content_id_index = __esm({
8287
8973
  });
8288
8974
 
8289
8975
  // dist/schema/meta-loader.js
8290
- import { promises as fs19 } from "fs";
8976
+ import { promises as fs20 } from "fs";
8291
8977
  import { join as join4 } from "pathe";
8292
8978
  import { z as z8 } from "zod";
8293
8979
  import chokidar2 from "chokidar";
@@ -8297,7 +8983,7 @@ function stripEmbeddedIdFromName2(name) {
8297
8983
  async function scanForCollectionMeta2(baseDir, relativePath = "") {
8298
8984
  const collections = [];
8299
8985
  try {
8300
- const entries = await fs19.readdir(baseDir, { withFileTypes: true });
8986
+ const entries = await fs20.readdir(baseDir, { withFileTypes: true });
8301
8987
  for (const entry of entries) {
8302
8988
  if (!entry.isDirectory())
8303
8989
  continue;
@@ -8308,8 +8994,8 @@ async function scanForCollectionMeta2(baseDir, relativePath = "") {
8308
8994
  const absolutePath = join4(baseDir, folderName);
8309
8995
  const metaPath = join4(absolutePath, ".collection.json");
8310
8996
  try {
8311
- await fs19.access(metaPath);
8312
- const content = await fs19.readFile(metaPath, "utf-8");
8997
+ await fs20.access(metaPath);
8998
+ const content = await fs20.readFile(metaPath, "utf-8");
8313
8999
  const parsed = JSON.parse(content);
8314
9000
  const meta = collectionMetaSchema2.parse(parsed);
8315
9001
  collections.push({
@@ -8341,7 +9027,7 @@ async function loadCollectionMetaFiles2(contentRoot) {
8341
9027
  let root = null;
8342
9028
  const rootMetaPath = join4(contentRoot, ".collection.json");
8343
9029
  try {
8344
- await fs19.access(rootMetaPath);
9030
+ await fs20.access(rootMetaPath);
8345
9031
  } catch (err) {
8346
9032
  if (err.code === "ENOENT") {
8347
9033
  } else {
@@ -8349,7 +9035,7 @@ async function loadCollectionMetaFiles2(contentRoot) {
8349
9035
  }
8350
9036
  }
8351
9037
  try {
8352
- const content = await fs19.readFile(rootMetaPath, "utf-8");
9038
+ const content = await fs20.readFile(rootMetaPath, "utf-8");
8353
9039
  const parsed = JSON.parse(content);
8354
9040
  root = rootCollectionMetaSchema2.parse(parsed);
8355
9041
  } catch (err) {
@@ -8496,7 +9182,12 @@ var init_field3 = __esm({
8496
9182
  collections: z9.array(z9.string().min(1)).min(1).optional(),
8497
9183
  entryTypes: z9.array(z9.string().min(1)).min(1).optional(),
8498
9184
  displayField: z9.string().min(1).optional(),
8499
- options: z9.array(referenceOptionSchema3).optional()
9185
+ options: z9.array(referenceOptionSchema3).optional(),
9186
+ // Must be listed here, not just on ReferenceFieldConfig: zod strips unknown keys by
9187
+ // default, so a consumer that adopts this schema's parse output would silently delete a
9188
+ // runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
9189
+ // absent on purpose — it is type-inference-only and stripping it changes no behavior.)
9190
+ includeBody: z9.boolean().optional()
8500
9191
  });
8501
9192
  fieldHolder3 = [z9.never()];
8502
9193
  blockSchema3 = z9.object({
@@ -8606,7 +9297,7 @@ var init_media3 = __esm({
8606
9297
 
8607
9298
  // dist/config/schemas/config.js
8608
9299
  import { z as z12 } from "zod";
8609
- var defaultBranchAccessSchema3, defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3, defaultPathAccessSchema3, defaultBaseBranchSchema3, defaultRemoteNameSchema3, defaultRemoteUrlSchema3, gitBotAuthorNameSchema3, gitBotAuthorEmailSchema3, githubTokenEnvVarSchema3, operatingModeSchema3, deployedAsSchema3, contentRootSchema3, sourceRootSchema3, deploymentNameSchema3, devContentSyncSchema3, devConfigSchema3, editorConfigSchema3, CanopyConfigSchema3, DEFAULT_PROD_WORKSPACE3;
9300
+ var defaultBranchAccessSchema3, defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3, defaultPathAccessSchema3, defaultBaseBranchSchema3, defaultRemoteNameSchema3, defaultRemoteUrlSchema3, gitBotAuthorNameSchema3, gitBotAuthorEmailSchema3, githubTokenEnvVarSchema3, operatingModeSchema3, deployedAsSchema3, contentRootSchema3, sourceRootSchema3, basePathSchema3, deploymentNameSchema3, devContentSyncSchema3, devConfigSchema3, editorConfigSchema3, CanopyConfigSchema3, DEFAULT_PROD_WORKSPACE3;
8610
9301
  var init_config4 = __esm({
8611
9302
  "dist/config/schemas/config.js"() {
8612
9303
  "use strict";
@@ -8630,6 +9321,9 @@ var init_config4 = __esm({
8630
9321
  deployedAsSchema3 = z12.enum(["static", "server"]).default("server");
8631
9322
  contentRootSchema3 = relativePathSchema3.default("content");
8632
9323
  sourceRootSchema3 = z12.string().min(1).optional();
9324
+ basePathSchema3 = z12.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
9325
+ message: "basePath must not be empty or only whitespace"
9326
+ }).optional();
8633
9327
  deploymentNameSchema3 = z12.string().default("prod");
8634
9328
  devContentSyncSchema3 = z12.enum(["off", "warn"]).default("warn");
8635
9329
  devConfigSchema3 = z12.object({
@@ -8692,6 +9386,7 @@ var init_config4 = __esm({
8692
9386
  deploymentName: deploymentNameSchema3.optional(),
8693
9387
  contentRoot: contentRootSchema3.default("content"),
8694
9388
  sourceRoot: sourceRootSchema3.optional(),
9389
+ basePath: basePathSchema3,
8695
9390
  editor: editorConfigSchema3.optional(),
8696
9391
  authPlugin: z12.custom().optional(),
8697
9392
  entryLinkUrl: z12.custom().optional(),
@@ -8778,13 +9473,13 @@ var init_content_index_generation = __esm({
8778
9473
  });
8779
9474
 
8780
9475
  // dist/sync-core.js
8781
- import fs20 from "node:fs/promises";
8782
- import path24 from "node:path";
9476
+ import fs21 from "node:fs/promises";
9477
+ import path25 from "node:path";
8783
9478
  import { simpleGit as simpleGit4 } from "simple-git";
8784
9479
  function assertWithinDir(resolved, parent, label) {
8785
- const normalizedResolved = path24.resolve(resolved);
8786
- const normalizedParent = path24.resolve(parent);
8787
- if (!normalizedResolved.startsWith(normalizedParent + path24.sep) && normalizedResolved !== normalizedParent) {
9480
+ const normalizedResolved = path25.resolve(resolved);
9481
+ const normalizedParent = path25.resolve(parent);
9482
+ if (!normalizedResolved.startsWith(normalizedParent + path25.sep) && normalizedResolved !== normalizedParent) {
8788
9483
  throw new Error(`${label} escapes the expected directory: ${resolved}`);
8789
9484
  }
8790
9485
  }
@@ -8792,19 +9487,19 @@ async function safeReplaceDir(oldDir, newDir) {
8792
9487
  const backupDir = `${oldDir}.sync-backup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
8793
9488
  const oldExists = await filePathExists(oldDir);
8794
9489
  if (oldExists) {
8795
- await fs20.rename(oldDir, backupDir);
9490
+ await fs21.rename(oldDir, backupDir);
8796
9491
  }
8797
9492
  try {
8798
- await fs20.rename(newDir, oldDir);
9493
+ await fs21.rename(newDir, oldDir);
8799
9494
  } catch (err) {
8800
9495
  if (oldExists) {
8801
- await fs20.rename(backupDir, oldDir).catch(() => {
9496
+ await fs21.rename(backupDir, oldDir).catch(() => {
8802
9497
  });
8803
9498
  }
8804
9499
  throw err;
8805
9500
  }
8806
9501
  if (oldExists) {
8807
- await fs20.rm(backupDir, { recursive: true, force: true }).catch(() => {
9502
+ await fs21.rm(backupDir, { recursive: true, force: true }).catch(() => {
8808
9503
  });
8809
9504
  }
8810
9505
  }
@@ -8812,7 +9507,7 @@ async function listFilesRecursive(dir, prefix = "") {
8812
9507
  const results = [];
8813
9508
  let entries;
8814
9509
  try {
8815
- entries = await fs20.readdir(dir, { withFileTypes: true });
9510
+ entries = await fs21.readdir(dir, { withFileTypes: true });
8816
9511
  } catch {
8817
9512
  return results;
8818
9513
  }
@@ -8823,7 +9518,7 @@ async function listFilesRecursive(dir, prefix = "") {
8823
9518
  continue;
8824
9519
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
8825
9520
  if (entry.isDirectory()) {
8826
- results.push(...await listFilesRecursive(path24.join(dir, entry.name), rel));
9521
+ results.push(...await listFilesRecursive(path25.join(dir, entry.name), rel));
8827
9522
  } else {
8828
9523
  results.push(rel);
8829
9524
  }
@@ -8831,19 +9526,19 @@ async function listFilesRecursive(dir, prefix = "") {
8831
9526
  return results;
8832
9527
  }
8833
9528
  async function copyDir(src, dest) {
8834
- await fs20.mkdir(dest, { recursive: true });
8835
- const entries = await fs20.readdir(src, { withFileTypes: true });
9529
+ await fs21.mkdir(dest, { recursive: true });
9530
+ const entries = await fs21.readdir(src, { withFileTypes: true });
8836
9531
  for (const entry of entries) {
8837
9532
  if (entry.name === ".git")
8838
9533
  continue;
8839
9534
  if (entry.isSymbolicLink())
8840
9535
  continue;
8841
- const srcPath = path24.join(src, entry.name);
8842
- const destPath = path24.join(dest, entry.name);
9536
+ const srcPath = path25.join(src, entry.name);
9537
+ const destPath = path25.join(dest, entry.name);
8843
9538
  if (entry.isDirectory()) {
8844
9539
  await copyDir(srcPath, destPath);
8845
9540
  } else {
8846
- await fs20.copyFile(srcPath, destPath);
9541
+ await fs21.copyFile(srcPath, destPath);
8847
9542
  }
8848
9543
  }
8849
9544
  }
@@ -8852,14 +9547,14 @@ async function pushContentToWorkspace(options) {
8852
9547
  if (!await filePathExists(srcContentDir)) {
8853
9548
  return { fileCount: 0 };
8854
9549
  }
8855
- const wsContentDir = path24.join(branchPath, contentRoot);
9550
+ const wsContentDir = path25.join(branchPath, contentRoot);
8856
9551
  assertWithinDir(wsContentDir, branchPath, "content-root");
8857
9552
  const tmpDir = `${wsContentDir}.sync-tmp-${Date.now()}`;
8858
9553
  try {
8859
9554
  await copyDir(srcContentDir, tmpDir);
8860
9555
  await safeReplaceDir(wsContentDir, tmpDir);
8861
9556
  } catch (err) {
8862
- await fs20.rm(tmpDir, { recursive: true, force: true }).catch(() => {
9557
+ await fs21.rm(tmpDir, { recursive: true, force: true }).catch(() => {
8863
9558
  });
8864
9559
  throw err;
8865
9560
  }
@@ -8903,19 +9598,21 @@ import lockfile3 from "proper-lockfile";
8903
9598
  var init_provisioning_lock = __esm({
8904
9599
  "dist/utils/provisioning-lock.js"() {
8905
9600
  "use strict";
9601
+ init_error();
9602
+ init_logger3();
8906
9603
  }
8907
9604
  });
8908
9605
 
8909
9606
  // dist/git-manager.js
8910
- import fs21 from "node:fs/promises";
8911
- import path25 from "node:path";
9607
+ import fs24 from "node:fs/promises";
9608
+ import path26 from "node:path";
8912
9609
  import { simpleGit as simpleGit6 } from "simple-git";
8913
9610
  async function ensureGitExcludePattern2(repoPath, pattern) {
8914
- const excludePath = path25.join(repoPath, ".git", "info", "exclude");
8915
- await fs21.mkdir(path25.dirname(excludePath), { recursive: true });
9611
+ const excludePath = path26.join(repoPath, ".git", "info", "exclude");
9612
+ await fs24.mkdir(path26.dirname(excludePath), { recursive: true });
8916
9613
  let content = "";
8917
9614
  try {
8918
- content = await fs21.readFile(excludePath, "utf-8");
9615
+ content = await fs24.readFile(excludePath, "utf-8");
8919
9616
  } catch (err) {
8920
9617
  if (!isNotFoundError(err))
8921
9618
  throw err;
@@ -8927,7 +9624,7 @@ async function ensureGitExcludePattern2(repoPath, pattern) {
8927
9624
  }
8928
9625
  const needsLeadingNewline = content.length > 0 && !content.endsWith("\n");
8929
9626
  const newContent = content + (needsLeadingNewline ? "\n" : "") + pattern + "\n";
8930
- await fs21.writeFile(excludePath, newContent, "utf-8");
9627
+ await fs24.writeFile(excludePath, newContent, "utf-8");
8931
9628
  log9.debug("git", "Added pattern to .git/info/exclude", { pattern });
8932
9629
  }
8933
9630
  var log9;
@@ -9084,7 +9781,7 @@ var init_config5 = __esm({
9084
9781
  });
9085
9782
 
9086
9783
  // dist/operating-mode/client-unsafe-strategy.js
9087
- import path26 from "node:path";
9784
+ import path27 from "node:path";
9088
9785
  function operatingStrategy3(mode) {
9089
9786
  const cached = strategyCache3.get(mode);
9090
9787
  if (cached)
@@ -9119,32 +9816,32 @@ var init_client_unsafe_strategy2 = __esm({
9119
9816
  // - shouldCommit(), shouldPush()
9120
9817
  // Add client-unsafe methods (use Node.js APIs)
9121
9818
  getWorkspaceRoot(_sourceRoot) {
9122
- return path26.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE3);
9819
+ return path27.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE3);
9123
9820
  }
9124
9821
  getContentRoot(contentRoot, sourceRoot) {
9125
- return path26.resolve(sourceRoot ?? process.cwd(), contentRoot);
9822
+ return path27.resolve(sourceRoot ?? process.cwd(), contentRoot);
9126
9823
  }
9127
9824
  getContentBranchesRoot(sourceRoot) {
9128
- return path26.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
9825
+ return path27.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
9129
9826
  }
9130
9827
  getContentBranchRoot(branchName, sourceRoot) {
9131
- return path26.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
9828
+ return path27.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
9132
9829
  }
9133
9830
  getGitExcludePattern() {
9134
9831
  return ".canopy-meta/";
9135
9832
  }
9136
9833
  getPermissionsFilePath(root) {
9137
- return path26.join(root, this.getPermissionsFileName());
9834
+ return path27.join(root, this.getPermissionsFileName());
9138
9835
  }
9139
9836
  getGroupsFilePath(root) {
9140
- return path26.join(root, this.getGroupsFileName());
9837
+ return path27.join(root, this.getGroupsFileName());
9141
9838
  }
9142
9839
  getRemoteUrlConfig() {
9143
9840
  return {
9144
9841
  shouldAutoInitLocal: false,
9145
9842
  defaultRemotePath: "",
9146
9843
  envVarName: "CANOPYCMS_REMOTE_URL",
9147
- autoDetectRemotePath: path26.join(this.getWorkspaceRoot(), "remote.git")
9844
+ autoDetectRemotePath: path27.join(this.getWorkspaceRoot(), "remote.git")
9148
9845
  };
9149
9846
  }
9150
9847
  requiresExistingRepo() {
@@ -9156,7 +9853,7 @@ var init_client_unsafe_strategy2 = __esm({
9156
9853
  return `canopycms-settings-${resolveDeploymentName3(config, "prod")}`;
9157
9854
  }
9158
9855
  getSettingsRoot(sourceRoot) {
9159
- return path26.join(this.getWorkspaceRoot(sourceRoot), "settings");
9856
+ return path27.join(this.getWorkspaceRoot(sourceRoot), "settings");
9160
9857
  }
9161
9858
  usesSeparateSettingsBranch() {
9162
9859
  return true;
@@ -9173,25 +9870,25 @@ var init_client_unsafe_strategy2 = __esm({
9173
9870
  DevStrategy3 = class extends DevClientSafeStrategy3 {
9174
9871
  // Inherits client-safe methods from DevClientSafeStrategy
9175
9872
  getWorkspaceRoot(sourceRoot) {
9176
- return path26.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
9873
+ return path27.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
9177
9874
  }
9178
9875
  getContentRoot(contentRoot, sourceRoot) {
9179
- return path26.resolve(sourceRoot ?? process.cwd(), contentRoot);
9876
+ return path27.resolve(sourceRoot ?? process.cwd(), contentRoot);
9180
9877
  }
9181
9878
  getContentBranchesRoot(sourceRoot) {
9182
- return path26.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
9879
+ return path27.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
9183
9880
  }
9184
9881
  getContentBranchRoot(branchName, sourceRoot) {
9185
- return path26.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
9882
+ return path27.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
9186
9883
  }
9187
9884
  getGitExcludePattern() {
9188
9885
  return ".canopy-meta/";
9189
9886
  }
9190
9887
  getPermissionsFilePath(root) {
9191
- return path26.join(root, this.getPermissionsFileName());
9888
+ return path27.join(root, this.getPermissionsFileName());
9192
9889
  }
9193
9890
  getGroupsFilePath(root) {
9194
- return path26.join(root, this.getGroupsFileName());
9891
+ return path27.join(root, this.getGroupsFileName());
9195
9892
  }
9196
9893
  getRemoteUrlConfig() {
9197
9894
  return {
@@ -9209,7 +9906,7 @@ var init_client_unsafe_strategy2 = __esm({
9209
9906
  return `canopycms-settings-${resolveDeploymentName3(config, "local")}`;
9210
9907
  }
9211
9908
  getSettingsRoot(sourceRoot) {
9212
- return path26.join(this.getWorkspaceRoot(sourceRoot), "settings");
9909
+ return path27.join(this.getWorkspaceRoot(sourceRoot), "settings");
9213
9910
  }
9214
9911
  usesSeparateSettingsBranch() {
9215
9912
  return true;
@@ -9241,8 +9938,8 @@ __export(sync_exports, {
9241
9938
  SyncError: () => SyncError,
9242
9939
  sync: () => sync
9243
9940
  });
9244
- import fs24 from "node:fs/promises";
9245
- import path27 from "node:path";
9941
+ import fs25 from "node:fs/promises";
9942
+ import path28 from "node:path";
9246
9943
  import { simpleGit as simpleGit7 } from "simple-git";
9247
9944
  import * as p2 from "@clack/prompts";
9248
9945
  async function detectCurrentBranch(projectDir) {
@@ -9255,7 +9952,7 @@ async function detectCurrentBranch(projectDir) {
9255
9952
  async function selectBranch(options, branchesDir) {
9256
9953
  let branches = [];
9257
9954
  if (await filePathExists(branchesDir)) {
9258
- const entries = await fs24.readdir(branchesDir, { withFileTypes: true });
9955
+ const entries = await fs25.readdir(branchesDir, { withFileTypes: true });
9259
9956
  branches = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
9260
9957
  }
9261
9958
  let branchName = options.branch;
@@ -9283,11 +9980,11 @@ async function selectBranch(options, branchesDir) {
9283
9980
  branchName = result;
9284
9981
  }
9285
9982
  }
9286
- const branchPath = path27.join(branchesDir, branchName);
9983
+ const branchPath = path28.join(branchesDir, branchName);
9287
9984
  assertWithinDir(branchPath, branchesDir, "--branch");
9288
9985
  if (!branches.includes(branchName)) {
9289
9986
  if (options.autoCreate) {
9290
- await fs24.mkdir(branchPath, { recursive: true });
9987
+ await fs25.mkdir(branchPath, { recursive: true });
9291
9988
  const wsGit = simpleGit7({ baseDir: branchPath });
9292
9989
  await wsGit.init();
9293
9990
  await wsGit.checkoutLocalBranch(branchName);
@@ -9304,8 +10001,8 @@ async function selectBranch(options, branchesDir) {
9304
10001
  async function syncPush(options) {
9305
10002
  const { projectDir } = options;
9306
10003
  const contentRoot = options.contentRoot || "content";
9307
- const branchesDir = path27.join(projectDir, ".canopy-dev", "content-branches");
9308
- const srcContentDir = path27.join(projectDir, contentRoot);
10004
+ const branchesDir = path28.join(projectDir, ".canopy-dev", "content-branches");
10005
+ const srcContentDir = path28.join(projectDir, contentRoot);
9309
10006
  assertWithinDir(srcContentDir, projectDir, "--content-root");
9310
10007
  if (!await filePathExists(srcContentDir)) {
9311
10008
  throw new SyncError(`Content directory not found: ${contentRoot}/ (expected at ${srcContentDir})`);
@@ -9313,7 +10010,7 @@ async function syncPush(options) {
9313
10010
  const branchName = await selectBranch({ ...options, projectDir, autoCreate: true }, branchesDir);
9314
10011
  if (!branchName)
9315
10012
  return { fileCount: 0 };
9316
- const branchPath = path27.join(branchesDir, branchName);
10013
+ const branchPath = path28.join(branchesDir, branchName);
9317
10014
  assertWithinDir(branchPath, branchesDir, "--branch");
9318
10015
  const wsGit = simpleGit7({ baseDir: branchPath });
9319
10016
  const status = await wsGit.status();
@@ -9371,19 +10068,19 @@ async function syncPush(options) {
9371
10068
  async function syncPull(options) {
9372
10069
  const { projectDir } = options;
9373
10070
  const contentRoot = options.contentRoot || "content";
9374
- const branchesDir = path27.join(projectDir, ".canopy-dev", "content-branches");
10071
+ const branchesDir = path28.join(projectDir, ".canopy-dev", "content-branches");
9375
10072
  const branchName = await selectBranch({ ...options, projectDir }, branchesDir);
9376
10073
  if (!branchName)
9377
10074
  return { fileCount: 0 };
9378
- const branchPath = path27.join(branchesDir, branchName);
10075
+ const branchPath = path28.join(branchesDir, branchName);
9379
10076
  assertWithinDir(branchPath, branchesDir, "--branch");
9380
- const branchContentDir = path27.join(branchPath, contentRoot);
10077
+ const branchContentDir = path28.join(branchPath, contentRoot);
9381
10078
  assertWithinDir(branchContentDir, branchPath, "--content-root");
9382
10079
  if (!await filePathExists(branchContentDir)) {
9383
10080
  throw new SyncError(`Content directory not found in branch workspace: ${branchName}/${contentRoot}`);
9384
10081
  }
9385
10082
  p2.log.step(`Pulling content from branch: ${branchName}`);
9386
- const destContentDir = path27.join(projectDir, contentRoot);
10083
+ const destContentDir = path28.join(projectDir, contentRoot);
9387
10084
  assertWithinDir(destContentDir, projectDir, "--content-root");
9388
10085
  const sourceGit = simpleGit7({ baseDir: projectDir });
9389
10086
  const status = await sourceGit.status();
@@ -9435,7 +10132,7 @@ async function syncPull(options) {
9435
10132
  await copyDir(branchContentDir, tmpDestDir);
9436
10133
  await safeReplaceDir(destContentDir, tmpDestDir);
9437
10134
  } catch (err) {
9438
- await fs24.rm(tmpDestDir, { recursive: true, force: true }).catch(() => {
10135
+ await fs25.rm(tmpDestDir, { recursive: true, force: true }).catch(() => {
9439
10136
  });
9440
10137
  throw err;
9441
10138
  }
@@ -9459,8 +10156,8 @@ async function syncPull(options) {
9459
10156
  async function syncBoth(options) {
9460
10157
  const { projectDir } = options;
9461
10158
  const contentRoot = options.contentRoot || "content";
9462
- const branchesDir = path27.join(projectDir, ".canopy-dev", "content-branches");
9463
- const srcContentDir = path27.join(projectDir, contentRoot);
10159
+ const branchesDir = path28.join(projectDir, ".canopy-dev", "content-branches");
10160
+ const srcContentDir = path28.join(projectDir, contentRoot);
9464
10161
  assertWithinDir(srcContentDir, projectDir, "--content-root");
9465
10162
  if (!await filePathExists(srcContentDir)) {
9466
10163
  throw new SyncError(`Content directory not found: ${contentRoot}/ (expected at ${srcContentDir})`);
@@ -9468,7 +10165,7 @@ async function syncBoth(options) {
9468
10165
  const branchName = await selectBranch({ ...options, projectDir, autoCreate: true }, branchesDir);
9469
10166
  if (!branchName)
9470
10167
  return { pushed: 0, pulled: 0 };
9471
- const branchPath = path27.join(branchesDir, branchName);
10168
+ const branchPath = path28.join(branchesDir, branchName);
9472
10169
  assertWithinDir(branchPath, branchesDir, "--branch");
9473
10170
  const wsGit = simpleGit7({ baseDir: branchPath });
9474
10171
  const status = await wsGit.status();
@@ -9503,14 +10200,14 @@ async function syncBoth(options) {
9503
10200
  try {
9504
10201
  const incomingBranch = `sync-incoming-${Date.now()}`;
9505
10202
  await wsGit.raw(["checkout", "-b", incomingBranch, baseRef]);
9506
- const wsContentDir = path27.join(branchPath, contentRoot);
10203
+ const wsContentDir = path28.join(branchPath, contentRoot);
9507
10204
  assertWithinDir(wsContentDir, branchPath, "--content-root");
9508
10205
  const tmpDir = `${wsContentDir}.sync-tmp-${Date.now()}`;
9509
10206
  try {
9510
10207
  await copyDir(srcContentDir, tmpDir);
9511
10208
  await safeReplaceDir(wsContentDir, tmpDir);
9512
10209
  } catch (err) {
9513
- await fs24.rm(tmpDir, { recursive: true, force: true }).catch(() => {
10210
+ await fs25.rm(tmpDir, { recursive: true, force: true }).catch(() => {
9514
10211
  });
9515
10212
  await wsGit.checkout(currentBranch);
9516
10213
  await wsGit.raw(["branch", "-D", incomingBranch]).catch(() => {
@@ -9567,11 +10264,11 @@ async function syncBoth(options) {
9567
10264
  }
9568
10265
  async function syncAbort(options) {
9569
10266
  const { projectDir } = options;
9570
- const branchesDir = path27.join(projectDir, ".canopy-dev", "content-branches");
10267
+ const branchesDir = path28.join(projectDir, ".canopy-dev", "content-branches");
9571
10268
  const branchName = await selectBranch({ ...options, projectDir }, branchesDir);
9572
10269
  if (!branchName)
9573
10270
  return;
9574
- const branchPath = path27.join(branchesDir, branchName);
10271
+ const branchPath = path28.join(branchesDir, branchName);
9575
10272
  assertWithinDir(branchPath, branchesDir, "--branch");
9576
10273
  const wsGit = simpleGit7({ baseDir: branchPath });
9577
10274
  const status = await wsGit.status();
@@ -9699,8 +10396,8 @@ var init_async_mutex = __esm({
9699
10396
  });
9700
10397
 
9701
10398
  // dist/utils/occ-json-write.js
9702
- import fs25 from "node:fs/promises";
9703
- import path28 from "node:path";
10399
+ import fs26 from "node:fs/promises";
10400
+ import path29 from "node:path";
9704
10401
  import { randomUUID as randomUUID4 } from "node:crypto";
9705
10402
  import lockfile4 from "proper-lockfile";
9706
10403
  async function writeOccJsonFile2(filePath, payload, options) {
@@ -9709,29 +10406,29 @@ async function writeOccJsonFile2(filePath, payload, options) {
9709
10406
  const writeId = randomUUID4();
9710
10407
  const data = { ...payload, version: newVersion, writeId };
9711
10408
  const content = JSON.stringify(data, null, 2) + (trailingNewline ? "\n" : "");
9712
- const dir = path28.dirname(filePath);
9713
- await fs25.mkdir(dir, { recursive: true });
10409
+ const dir = path29.dirname(filePath);
10410
+ await fs26.mkdir(dir, { recursive: true });
9714
10411
  const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
9715
- await fs25.writeFile(tempPath, content, "utf-8");
10412
+ await fs26.writeFile(tempPath, content, "utf-8");
9716
10413
  if (expectedVersion === null) {
9717
10414
  try {
9718
- await fs25.link(tempPath, filePath);
10415
+ await fs26.link(tempPath, filePath);
9719
10416
  } catch (err) {
9720
- await fs25.unlink(tempPath).catch(() => {
10417
+ await fs26.unlink(tempPath).catch(() => {
9721
10418
  });
9722
10419
  if (isFileExistsError(err)) {
9723
10420
  throw new OccWriteConflictError2();
9724
10421
  }
9725
10422
  throw err;
9726
10423
  }
9727
- await fs25.unlink(tempPath).catch(() => {
10424
+ await fs26.unlink(tempPath).catch(() => {
9728
10425
  });
9729
10426
  return { version: newVersion, writeId };
9730
10427
  }
9731
10428
  try {
9732
10429
  let currentVersion = null;
9733
10430
  try {
9734
- const current = JSON.parse(await fs25.readFile(filePath, "utf-8"));
10431
+ const current = JSON.parse(await fs26.readFile(filePath, "utf-8"));
9735
10432
  currentVersion = current.version ?? 0;
9736
10433
  } catch {
9737
10434
  currentVersion = null;
@@ -9739,16 +10436,16 @@ async function writeOccJsonFile2(filePath, payload, options) {
9739
10436
  if (currentVersion !== expectedVersion) {
9740
10437
  throw new OccWriteConflictError2();
9741
10438
  }
9742
- await fs25.rename(tempPath, filePath);
10439
+ await fs26.rename(tempPath, filePath);
9743
10440
  if (settleMs > 0) {
9744
10441
  await new Promise((resolve2) => setTimeout(resolve2, settleMs));
9745
10442
  }
9746
- const afterWrite = JSON.parse(await fs25.readFile(filePath, "utf-8"));
10443
+ const afterWrite = JSON.parse(await fs26.readFile(filePath, "utf-8"));
9747
10444
  if (afterWrite.writeId !== writeId) {
9748
10445
  throw new OccWriteConflictError2();
9749
10446
  }
9750
10447
  } catch (err) {
9751
- await fs25.unlink(tempPath).catch(() => {
10448
+ await fs26.unlink(tempPath).catch(() => {
9752
10449
  });
9753
10450
  throw err;
9754
10451
  }
@@ -9774,8 +10471,8 @@ async function withOccRetry2(operation, options) {
9774
10471
  throw new Error("Unreachable");
9775
10472
  }
9776
10473
  async function withOccFileLock2(filePath, fn) {
9777
- const dir = path28.dirname(filePath);
9778
- await fs25.mkdir(dir, { recursive: true });
10474
+ const dir = path29.dirname(filePath);
10475
+ await fs26.mkdir(dir, { recursive: true });
9779
10476
  const MAX_ATTEMPTS = 14;
9780
10477
  let release = null;
9781
10478
  for (let attempt = 1; release === null; attempt++) {
@@ -9834,8 +10531,8 @@ var init_occ_json_write = __esm({
9834
10531
  });
9835
10532
 
9836
10533
  // dist/branch-registry.js
9837
- import fs26 from "node:fs/promises";
9838
- import path29 from "node:path";
10534
+ import fs27 from "node:fs/promises";
10535
+ import path30 from "node:path";
9839
10536
  var log12, REGISTRY_FILE2, REGISTRY_STALE_FILE2, REGISTRY_TEMP_FILE2, REGISTRY_VERSION2, RESOURCE4, GET_MISS_REFRESH_MIN_INTERVAL_MS2, BranchRegistry2;
9840
10537
  var init_branch_registry = __esm({
9841
10538
  "dist/branch-registry.js"() {
@@ -9856,10 +10553,10 @@ var init_branch_registry = __esm({
9856
10553
  constructor(root) {
9857
10554
  this.regenInFlight = null;
9858
10555
  this.lastForcedRefreshMs = 0;
9859
- this.root = path29.resolve(root);
9860
- this.registryPath = path29.join(this.root, REGISTRY_FILE2);
9861
- this.stalePath = path29.join(this.root, REGISTRY_STALE_FILE2);
9862
- this.tempPath = path29.join(this.root, REGISTRY_TEMP_FILE2);
10556
+ this.root = path30.resolve(root);
10557
+ this.registryPath = path30.join(this.root, REGISTRY_FILE2);
10558
+ this.stalePath = path30.join(this.root, REGISTRY_STALE_FILE2);
10559
+ this.tempPath = path30.join(this.root, REGISTRY_TEMP_FILE2);
9863
10560
  }
9864
10561
  /**
9865
10562
  * Returns all branches. Uses the cached snapshot if its embedded generation
@@ -9868,7 +10565,7 @@ var init_branch_registry = __esm({
9868
10565
  async list() {
9869
10566
  let parsed;
9870
10567
  try {
9871
- const raw = await fs26.readFile(this.registryPath, "utf8");
10568
+ const raw = await fs27.readFile(this.registryPath, "utf8");
9872
10569
  parsed = JSON.parse(raw);
9873
10570
  } catch (err) {
9874
10571
  if (isNotFoundError(err)) {
@@ -9957,7 +10654,7 @@ var init_branch_registry = __esm({
9957
10654
  async performRegenerate() {
9958
10655
  const read = await readResourceGeneration2(this.root, RESOURCE4);
9959
10656
  const branches = await this.scanBranchDirectories();
9960
- await fs26.unlink(this.stalePath).catch(() => {
10657
+ await fs27.unlink(this.stalePath).catch(() => {
9961
10658
  });
9962
10659
  if (!read.ok) {
9963
10660
  return branches;
@@ -9968,12 +10665,12 @@ var init_branch_registry = __esm({
9968
10665
  generation: read.token
9969
10666
  };
9970
10667
  const uniqueTempPath = `${this.tempPath}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
9971
- await fs26.mkdir(this.root, { recursive: true });
9972
- await fs26.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
10668
+ await fs27.mkdir(this.root, { recursive: true });
10669
+ await fs27.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
9973
10670
  try {
9974
- await fs26.rename(uniqueTempPath, this.registryPath);
10671
+ await fs27.rename(uniqueTempPath, this.registryPath);
9975
10672
  } catch (err) {
9976
- await fs26.unlink(uniqueTempPath).catch(() => {
10673
+ await fs27.unlink(uniqueTempPath).catch(() => {
9977
10674
  });
9978
10675
  throw err;
9979
10676
  }
@@ -9987,12 +10684,12 @@ var init_branch_registry = __esm({
9987
10684
  async scanBranchDirectories() {
9988
10685
  const branches = [];
9989
10686
  try {
9990
- const entries = await fs26.readdir(this.root, { withFileTypes: true });
10687
+ const entries = await fs27.readdir(this.root, { withFileTypes: true });
9991
10688
  for (const entry of entries) {
9992
10689
  if (!entry.isDirectory() || entry.name.startsWith(".")) {
9993
10690
  continue;
9994
10691
  }
9995
- const branchRoot = path29.join(this.root, entry.name);
10692
+ const branchRoot = path30.join(this.root, entry.name);
9996
10693
  let meta;
9997
10694
  try {
9998
10695
  meta = await BranchMetadataFileManager2.loadOnly(branchRoot);
@@ -10021,8 +10718,8 @@ var init_branch_registry = __esm({
10021
10718
  });
10022
10719
 
10023
10720
  // dist/branch-metadata.js
10024
- import fs27 from "node:fs/promises";
10025
- import path30 from "node:path";
10721
+ import fs28 from "node:fs/promises";
10722
+ import path31 from "node:path";
10026
10723
  var BRANCH_META_DIR2, BRANCH_META_FILE2, CURRENT_SCHEMA_VERSION2, BranchMetadataConflictError2, BranchMetadataCorruptError2, BranchMetadataFileManager2;
10027
10724
  var init_branch_metadata = __esm({
10028
10725
  "dist/branch-metadata.js"() {
@@ -10051,8 +10748,8 @@ var init_branch_metadata = __esm({
10051
10748
  };
10052
10749
  BranchMetadataFileManager2 = class _BranchMetadataFileManager2 {
10053
10750
  constructor(branchRoot, baseRoot, options) {
10054
- this.branchRoot = path30.resolve(branchRoot);
10055
- this.filePath = path30.join(this.branchRoot, BRANCH_META_DIR2, BRANCH_META_FILE2);
10751
+ this.branchRoot = path31.resolve(branchRoot);
10752
+ this.filePath = path31.join(this.branchRoot, BRANCH_META_DIR2, BRANCH_META_FILE2);
10056
10753
  this.baseRoot = baseRoot;
10057
10754
  this.settleMs = options?.settleMs;
10058
10755
  }
@@ -10061,10 +10758,10 @@ var init_branch_metadata = __esm({
10061
10758
  * Use this for read-only access (e.g., in registry scanning or loadBranchContext).
10062
10759
  */
10063
10760
  static async loadOnly(branchRoot) {
10064
- const resolvedRoot = path30.resolve(branchRoot);
10065
- const filePath = path30.join(resolvedRoot, BRANCH_META_DIR2, BRANCH_META_FILE2);
10761
+ const resolvedRoot = path31.resolve(branchRoot);
10762
+ const filePath = path31.join(resolvedRoot, BRANCH_META_DIR2, BRANCH_META_FILE2);
10066
10763
  try {
10067
- const raw = await fs27.readFile(filePath, "utf8");
10764
+ const raw = await fs28.readFile(filePath, "utf8");
10068
10765
  return JSON.parse(raw);
10069
10766
  } catch (err) {
10070
10767
  if (isNotFoundError(err)) {
@@ -10085,7 +10782,7 @@ var init_branch_metadata = __esm({
10085
10782
  }
10086
10783
  async load() {
10087
10784
  try {
10088
- const raw = await fs27.readFile(this.filePath, "utf8");
10785
+ const raw = await fs28.readFile(this.filePath, "utf8");
10089
10786
  const parsed = JSON.parse(raw);
10090
10787
  const version = parsed.version ?? 0;
10091
10788
  return { meta: parsed, version };
@@ -10147,7 +10844,7 @@ var init_branch_metadata = __esm({
10147
10844
  */
10148
10845
  async save(incoming) {
10149
10846
  try {
10150
- await fs27.stat(this.branchRoot);
10847
+ await fs28.stat(this.branchRoot);
10151
10848
  } catch (err) {
10152
10849
  if (isNotFoundError(err)) {
10153
10850
  throw new BranchMetadataConflictError2("Branch no longer exists");
@@ -10222,8 +10919,8 @@ __export(migrate_exports, {
10222
10919
  migrate: () => migrate,
10223
10920
  slugifyName: () => slugifyName
10224
10921
  });
10225
- import fs28 from "node:fs/promises";
10226
- import path31 from "node:path";
10922
+ import fs29 from "node:fs/promises";
10923
+ import path33 from "node:path";
10227
10924
  import * as p3 from "@clack/prompts";
10228
10925
  function slugifyName(name) {
10229
10926
  const slug = name.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
@@ -10231,7 +10928,7 @@ function slugifyName(name) {
10231
10928
  }
10232
10929
  function toValidSlug(name) {
10233
10930
  const slug = slugifyName(name);
10234
- const parsed = parseSlug(slug);
10931
+ const parsed = parseSlug2(slug);
10235
10932
  if (!parsed.ok) {
10236
10933
  throw new MigrateError(`Cannot derive a valid slug from "${name}" (got "${slug}"): ${parsed.error}`);
10237
10934
  }
@@ -10248,7 +10945,7 @@ function stripDirIdSuffix(dirName) {
10248
10945
  return dirName;
10249
10946
  }
10250
10947
  async function planDirectory(dirPath, isRoot, ctx) {
10251
- const entries = await fs28.readdir(dirPath, { withFileTypes: true });
10948
+ const entries = await fs29.readdir(dirPath, { withFileTypes: true });
10252
10949
  entries.sort((a, b) => a.name.localeCompare(b.name));
10253
10950
  const ops = [];
10254
10951
  let hasOwnFiles = false;
@@ -10257,7 +10954,7 @@ async function planDirectory(dirPath, isRoot, ctx) {
10257
10954
  if (entry.name.startsWith("."))
10258
10955
  continue;
10259
10956
  if (entry.isDirectory()) {
10260
- const subPath = path31.join(dirPath, entry.name);
10957
+ const subPath = path33.join(dirPath, entry.name);
10261
10958
  const sub = await planDirectory(subPath, false, ctx);
10262
10959
  if (!sub.hasContent)
10263
10960
  continue;
@@ -10265,10 +10962,10 @@ async function planDirectory(dirPath, isRoot, ctx) {
10265
10962
  ops.push(...sub.ops);
10266
10963
  if (!extractIdFromFilename2(entry.name)) {
10267
10964
  const newName = `${toValidSlug(entry.name)}.${generateId2()}`;
10268
- ops.push({ kind: "rename", from: subPath, to: path31.join(dirPath, newName) });
10965
+ ops.push({ kind: "rename", from: subPath, to: path33.join(dirPath, newName) });
10269
10966
  }
10270
10967
  } else if (entry.isFile()) {
10271
- const ext = path31.extname(entry.name).slice(1);
10968
+ const ext = path33.extname(entry.name).slice(1);
10272
10969
  const matchesFormat = ext === ctx.format || ctx.format === "yaml" && ext === "yml";
10273
10970
  if (!matchesFormat)
10274
10971
  continue;
@@ -10279,17 +10976,17 @@ async function planDirectory(dirPath, isRoot, ctx) {
10279
10976
  const newName = `${ctx.entryType}.${toValidSlug(base)}.${generateId2()}.${ext}`;
10280
10977
  ops.push({
10281
10978
  kind: "rename",
10282
- from: path31.join(dirPath, entry.name),
10283
- to: path31.join(dirPath, newName)
10979
+ from: path33.join(dirPath, entry.name),
10980
+ to: path33.join(dirPath, newName)
10284
10981
  });
10285
10982
  }
10286
10983
  }
10287
10984
  }
10288
10985
  const needsMeta = isRoot ? hasOwnFiles : hasContent;
10289
- const metaPath = path31.join(dirPath, ".collection.json");
10986
+ const metaPath = path33.join(dirPath, ".collection.json");
10290
10987
  if (needsMeta && !await filePathExists(metaPath)) {
10291
10988
  const entryTypeMeta = { name: ctx.entryType, format: ctx.format, schema: ctx.schema };
10292
- const meta = isRoot ? { entries: [entryTypeMeta] } : { name: toValidSlug(stripDirIdSuffix(path31.basename(dirPath))), entries: [entryTypeMeta] };
10989
+ const meta = isRoot ? { entries: [entryTypeMeta] } : { name: toValidSlug(stripDirIdSuffix(path33.basename(dirPath))), entries: [entryTypeMeta] };
10293
10990
  ops.push({ kind: "write", filePath: metaPath, content: JSON.stringify(meta, null, 2) + "\n" });
10294
10991
  }
10295
10992
  return { ops, hasContent };
@@ -10298,7 +10995,7 @@ async function migrate(options) {
10298
10995
  const { projectDir } = options;
10299
10996
  const contentRoot = options.contentRoot || "content";
10300
10997
  p3.intro("CanopyCMS migrate");
10301
- const contentDir = path31.join(projectDir, contentRoot);
10998
+ const contentDir = path33.join(projectDir, contentRoot);
10302
10999
  if (!await filePathExists(contentDir)) {
10303
11000
  throw new MigrateError(`Content directory not found: ${contentRoot}/ (expected at ${contentDir})`);
10304
11001
  }
@@ -10375,14 +11072,14 @@ async function migrate(options) {
10375
11072
  const applyOps = async () => {
10376
11073
  for (const op of ops) {
10377
11074
  if (op.kind === "rename") {
10378
- await fs28.rename(op.from, op.to);
11075
+ await fs29.rename(op.from, op.to);
10379
11076
  } else {
10380
- await fs28.writeFile(op.filePath, op.content, "utf-8");
11077
+ await fs29.writeFile(op.filePath, op.content, "utf-8");
10381
11078
  }
10382
11079
  }
10383
11080
  };
10384
11081
  if (isBranchClone) {
10385
- const lockPath = path31.join(projectDir, ".canopy-meta", "schema");
11082
+ const lockPath = path33.join(projectDir, ".canopy-meta", "schema");
10386
11083
  try {
10387
11084
  await withLock2(lockPath, () => withOccFileLock2(lockPath, applyOps));
10388
11085
  } catch (err) {
@@ -10430,7 +11127,7 @@ var init_migrate = __esm({
10430
11127
  this.name = "MigrateError";
10431
11128
  }
10432
11129
  };
10433
- describeOp = (op, baseDir) => op.kind === "rename" ? `rename ${path31.relative(baseDir, op.from)} \u2192 ${path31.basename(op.to)}` : `create ${path31.relative(baseDir, op.filePath)}`;
11130
+ describeOp = (op, baseDir) => op.kind === "rename" ? `rename ${path33.relative(baseDir, op.from)} \u2192 ${path33.basename(op.to)}` : `create ${path33.relative(baseDir, op.filePath)}`;
10434
11131
  }
10435
11132
  });
10436
11133