canopycms 0.0.63 → 0.0.65

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 (1211) hide show
  1. package/README.md +153 -19
  2. package/dist/ai/generate.d.ts +4 -5
  3. package/dist/ai/generate.js +0 -1
  4. package/dist/ai/handler.d.ts +3 -4
  5. package/dist/ai/handler.js +0 -1
  6. package/dist/ai/index.d.ts +7 -8
  7. package/dist/ai/index.js +0 -1
  8. package/dist/ai/json-to-markdown.d.ts +1 -2
  9. package/dist/ai/json-to-markdown.js +11 -2
  10. package/dist/ai/resolve-branch.d.ts +1 -2
  11. package/dist/ai/resolve-branch.js +0 -1
  12. package/dist/ai/strip-mdx.d.ts +0 -1
  13. package/dist/ai/strip-mdx.js +0 -1
  14. package/dist/ai/to-plain-text.d.ts +4 -1
  15. package/dist/ai/to-plain-text.js +47 -1
  16. package/dist/ai/transform-components.d.ts +1 -2
  17. package/dist/ai/transform-components.js +0 -1
  18. package/dist/ai/types.d.ts +1 -2
  19. package/dist/ai/types.js +0 -1
  20. package/dist/api/admin-branch-health.d.ts +7 -8
  21. package/dist/api/admin-branch-health.js +0 -1
  22. package/dist/api/admin.d.ts +16 -17
  23. package/dist/api/admin.js +0 -1
  24. package/dist/api/assets.d.ts +8 -9
  25. package/dist/api/assets.js +9 -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 +14 -3
  110. package/dist/auth/index.d.ts +6 -7
  111. package/dist/auth/index.js +0 -1
  112. package/dist/auth/plugin.d.ts +3 -4
  113. package/dist/auth/plugin.js +0 -1
  114. package/dist/auth/types.d.ts +1 -2
  115. package/dist/auth/types.js +0 -1
  116. package/dist/authorization/branch.d.ts +4 -5
  117. package/dist/authorization/branch.js +0 -1
  118. package/dist/authorization/content.d.ts +6 -7
  119. package/dist/authorization/content.js +0 -1
  120. package/dist/authorization/groups/index.d.ts +2 -3
  121. package/dist/authorization/groups/index.js +0 -1
  122. package/dist/authorization/groups/loader.d.ts +3 -4
  123. package/dist/authorization/groups/loader.js +0 -1
  124. package/dist/authorization/groups/schema.d.ts +1 -2
  125. package/dist/authorization/groups/schema.js +0 -1
  126. package/dist/authorization/helpers.d.ts +0 -1
  127. package/dist/authorization/helpers.js +0 -1
  128. package/dist/authorization/index.d.ts +11 -12
  129. package/dist/authorization/index.js +0 -1
  130. package/dist/authorization/path.d.ts +4 -5
  131. package/dist/authorization/path.js +0 -1
  132. package/dist/authorization/permissions/index.d.ts +2 -3
  133. package/dist/authorization/permissions/index.js +0 -1
  134. package/dist/authorization/permissions/loader.d.ts +4 -5
  135. package/dist/authorization/permissions/loader.js +0 -1
  136. package/dist/authorization/permissions/schema.d.ts +2 -3
  137. package/dist/authorization/permissions/schema.js +0 -1
  138. package/dist/authorization/protected-branch.d.ts +2 -3
  139. package/dist/authorization/protected-branch.js +0 -1
  140. package/dist/authorization/settings-file-store.d.ts +1 -2
  141. package/dist/authorization/settings-file-store.js +0 -1
  142. package/dist/authorization/types.d.ts +6 -7
  143. package/dist/authorization/types.js +0 -1
  144. package/dist/authorization/validation.d.ts +1 -2
  145. package/dist/authorization/validation.js +0 -1
  146. package/dist/branch-health.d.ts +22 -3
  147. package/dist/branch-health.js +6 -2
  148. package/dist/branch-metadata.d.ts +2 -3
  149. package/dist/branch-metadata.js +0 -1
  150. package/dist/branch-registry.d.ts +1 -2
  151. package/dist/branch-registry.js +0 -1
  152. package/dist/branch-schema-cache.d.ts +4 -5
  153. package/dist/branch-schema-cache.js +0 -1
  154. package/dist/branch-workspace.d.ts +4 -5
  155. package/dist/branch-workspace.js +0 -1
  156. package/dist/build/generate-ai-content.d.ts +21 -5
  157. package/dist/build/generate-ai-content.js +98 -6
  158. package/dist/build/index.d.ts +2 -3
  159. package/dist/build/index.js +0 -1
  160. package/dist/build-canopy.d.ts +5 -6
  161. package/dist/build-canopy.js +0 -1
  162. package/dist/build-mode.d.ts +1 -2
  163. package/dist/build-mode.js +0 -1
  164. package/dist/cli/cli.d.ts +18 -2
  165. package/dist/cli/cli.js +1172 -490
  166. package/dist/cli/generate-ai-content.d.ts +0 -1
  167. package/dist/cli/generate-ai-content.js +1000 -360
  168. package/dist/cli/init.d.ts +1 -2
  169. package/dist/cli/init.js +43 -6
  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/Dockerfile.cms.template +8 -2
  179. package/dist/cli/template-files/cms-stack.ts.template +33 -6
  180. package/dist/cli/template-files/deploy-cms.yml.template +29 -14
  181. package/dist/cli/templates.d.ts +0 -1
  182. package/dist/cli/templates.js +0 -1
  183. package/dist/client.d.ts +13 -14
  184. package/dist/client.js +0 -1
  185. package/dist/comment-store.d.ts +0 -1
  186. package/dist/comment-store.js +0 -1
  187. package/dist/config/flatten.d.ts +1 -2
  188. package/dist/config/flatten.js +0 -1
  189. package/dist/config/helpers.d.ts +3 -4
  190. package/dist/config/helpers.js +2 -2
  191. package/dist/config/index.d.ts +10 -11
  192. package/dist/config/index.js +0 -1
  193. package/dist/config/schemas/collection.d.ts +0 -1
  194. package/dist/config/schemas/collection.js +0 -1
  195. package/dist/config/schemas/config.d.ts +7 -4
  196. package/dist/config/schemas/config.js +12 -1
  197. package/dist/config/schemas/field.d.ts +3 -1
  198. package/dist/config/schemas/field.js +5 -1
  199. package/dist/config/schemas/media.d.ts +0 -1
  200. package/dist/config/schemas/media.js +0 -1
  201. package/dist/config/schemas/permissions.d.ts +0 -1
  202. package/dist/config/schemas/permissions.js +0 -1
  203. package/dist/config/types.d.ts +55 -9
  204. package/dist/config/types.js +0 -1
  205. package/dist/config/validation.d.ts +1 -2
  206. package/dist/config/validation.js +0 -1
  207. package/dist/config.d.ts +2 -3
  208. package/dist/config.js +0 -1
  209. package/dist/content-id-index.d.ts +1 -2
  210. package/dist/content-id-index.js +0 -1
  211. package/dist/content-index-generation.d.ts +0 -1
  212. package/dist/content-index-generation.js +0 -1
  213. package/dist/content-index-registry.d.ts +0 -1
  214. package/dist/content-index-registry.js +0 -1
  215. package/dist/content-listing.d.ts +75 -59
  216. package/dist/content-listing.js +76 -83
  217. package/dist/content-reader.d.ts +4 -5
  218. package/dist/content-reader.js +22 -3
  219. package/dist/content-store.d.ts +137 -6
  220. package/dist/content-store.js +294 -22
  221. package/dist/content-tree.d.ts +15 -5
  222. package/dist/content-tree.js +27 -8
  223. package/dist/context.d.ts +9 -6
  224. package/dist/context.js +0 -1
  225. package/dist/dev-content-watcher.d.ts +12 -3
  226. package/dist/dev-content-watcher.js +120 -30
  227. package/dist/editor/BranchManager.d.ts +4 -5
  228. package/dist/editor/BranchManager.js +0 -1
  229. package/dist/editor/CanopyEditor.d.ts +2 -3
  230. package/dist/editor/CanopyEditor.js +1 -2
  231. package/dist/editor/CanopyEditorPage.d.ts +2 -3
  232. package/dist/editor/CanopyEditorPage.js +0 -1
  233. package/dist/editor/CommentsPanel.d.ts +2 -3
  234. package/dist/editor/CommentsPanel.js +0 -1
  235. package/dist/editor/Editor.d.ts +13 -8
  236. package/dist/editor/Editor.js +4 -5
  237. package/dist/editor/EditorPanes.d.ts +0 -1
  238. package/dist/editor/EditorPanes.js +0 -1
  239. package/dist/editor/EntryNavigator.d.ts +1 -2
  240. package/dist/editor/EntryNavigator.js +0 -1
  241. package/dist/editor/FormRenderer.d.ts +2 -3
  242. package/dist/editor/FormRenderer.js +22 -3
  243. package/dist/editor/GroupManager.d.ts +2 -3
  244. package/dist/editor/GroupManager.js +0 -1
  245. package/dist/editor/PermissionManager.d.ts +2 -3
  246. package/dist/editor/PermissionManager.js +0 -1
  247. package/dist/editor/admin/SystemHealthPanel.d.ts +0 -1
  248. package/dist/editor/admin/SystemHealthPanel.js +0 -1
  249. package/dist/editor/admin/useSystemHealth.d.ts +2 -3
  250. package/dist/editor/admin/useSystemHealth.js +0 -1
  251. package/dist/editor/canopy-path.d.ts +0 -1
  252. package/dist/editor/canopy-path.js +0 -1
  253. package/dist/editor/client-reference-resolver.d.ts +13 -4
  254. package/dist/editor/client-reference-resolver.js +18 -8
  255. package/dist/editor/comments/BranchComments.d.ts +2 -3
  256. package/dist/editor/comments/BranchComments.js +0 -1
  257. package/dist/editor/comments/EntryComments.d.ts +1 -2
  258. package/dist/editor/comments/EntryComments.js +0 -1
  259. package/dist/editor/comments/FieldWrapper.d.ts +1 -2
  260. package/dist/editor/comments/FieldWrapper.js +0 -1
  261. package/dist/editor/comments/InlineCommentThread.d.ts +2 -3
  262. package/dist/editor/comments/InlineCommentThread.js +0 -1
  263. package/dist/editor/comments/ThreadCarousel.d.ts +2 -3
  264. package/dist/editor/comments/ThreadCarousel.js +0 -1
  265. package/dist/editor/components/ConfirmDeleteModal.d.ts +0 -1
  266. package/dist/editor/components/ConfirmDeleteModal.js +0 -1
  267. package/dist/editor/components/EditorFooter.d.ts +0 -1
  268. package/dist/editor/components/EditorFooter.js +0 -1
  269. package/dist/editor/components/EditorHeader.d.ts +4 -5
  270. package/dist/editor/components/EditorHeader.js +0 -1
  271. package/dist/editor/components/EditorSidebar.d.ts +1 -2
  272. package/dist/editor/components/EditorSidebar.js +0 -1
  273. package/dist/editor/components/EntryCreateModal.d.ts +0 -1
  274. package/dist/editor/components/EntryCreateModal.js +21 -10
  275. package/dist/editor/components/RenameEntryModal.d.ts +0 -1
  276. package/dist/editor/components/RenameEntryModal.js +0 -1
  277. package/dist/editor/components/UserBadge.d.ts +2 -3
  278. package/dist/editor/components/UserBadge.js +0 -1
  279. package/dist/editor/components/index.d.ts +3 -4
  280. package/dist/editor/components/index.js +0 -1
  281. package/dist/editor/context/ApiClientContext.d.ts +9 -3
  282. package/dist/editor/context/ApiClientContext.js +8 -4
  283. package/dist/editor/context/AssetContext.d.ts +26 -2
  284. package/dist/editor/context/AssetContext.js +23 -3
  285. package/dist/editor/context/EditorStateContext.d.ts +1 -2
  286. package/dist/editor/context/EditorStateContext.js +0 -1
  287. package/dist/editor/context/SWRProvider.d.ts +0 -1
  288. package/dist/editor/context/SWRProvider.js +0 -1
  289. package/dist/editor/context/index.d.ts +4 -5
  290. package/dist/editor/context/index.js +0 -1
  291. package/dist/editor/editor-config.d.ts +2 -3
  292. package/dist/editor/editor-config.js +0 -1
  293. package/dist/editor/editor-utils.d.ts +22 -10
  294. package/dist/editor/editor-utils.js +41 -10
  295. package/dist/editor/fields/BlockField.d.ts +1 -2
  296. package/dist/editor/fields/BlockField.js +0 -1
  297. package/dist/editor/fields/CodeField.d.ts +0 -1
  298. package/dist/editor/fields/CodeField.js +0 -1
  299. package/dist/editor/fields/DateTimeField.d.ts +0 -1
  300. package/dist/editor/fields/DateTimeField.js +0 -1
  301. package/dist/editor/fields/ImageField.d.ts +1 -2
  302. package/dist/editor/fields/ImageField.js +0 -1
  303. package/dist/editor/fields/InlineGroupField.d.ts +2 -3
  304. package/dist/editor/fields/InlineGroupField.js +0 -1
  305. package/dist/editor/fields/MarkdownField.d.ts +0 -1
  306. package/dist/editor/fields/MarkdownField.js +0 -1
  307. package/dist/editor/fields/MdxImageDialog.d.ts +0 -1
  308. package/dist/editor/fields/MdxImageDialog.js +0 -1
  309. package/dist/editor/fields/NumberField.d.ts +0 -1
  310. package/dist/editor/fields/NumberField.js +0 -1
  311. package/dist/editor/fields/NumberListField.d.ts +0 -1
  312. package/dist/editor/fields/NumberListField.js +0 -1
  313. package/dist/editor/fields/ObjectField.d.ts +11 -2
  314. package/dist/editor/fields/ObjectField.js +3 -4
  315. package/dist/editor/fields/ReferenceField.d.ts +0 -1
  316. package/dist/editor/fields/ReferenceField.js +8 -3
  317. package/dist/editor/fields/SelectField.d.ts +0 -1
  318. package/dist/editor/fields/SelectField.js +0 -1
  319. package/dist/editor/fields/StringListField.d.ts +0 -1
  320. package/dist/editor/fields/StringListField.js +0 -1
  321. package/dist/editor/fields/TextField.d.ts +0 -1
  322. package/dist/editor/fields/TextField.js +0 -1
  323. package/dist/editor/fields/ToggleField.d.ts +0 -1
  324. package/dist/editor/fields/ToggleField.js +0 -1
  325. package/dist/editor/fields/entry-link/EntryLinkContext.d.ts +1 -2
  326. package/dist/editor/fields/entry-link/EntryLinkContext.js +0 -1
  327. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +0 -1
  328. package/dist/editor/fields/entry-link/InsertEntryLink.js +0 -1
  329. package/dist/editor/fields/entry-link/index.d.ts +2 -3
  330. package/dist/editor/fields/entry-link/index.js +0 -1
  331. package/dist/editor/group-manager/ExternalGroupsTab.d.ts +1 -2
  332. package/dist/editor/group-manager/ExternalGroupsTab.js +0 -1
  333. package/dist/editor/group-manager/GroupCard.d.ts +1 -2
  334. package/dist/editor/group-manager/GroupCard.js +0 -1
  335. package/dist/editor/group-manager/GroupForm.d.ts +1 -2
  336. package/dist/editor/group-manager/GroupForm.js +0 -1
  337. package/dist/editor/group-manager/InternalGroupsTab.d.ts +1 -2
  338. package/dist/editor/group-manager/InternalGroupsTab.js +0 -1
  339. package/dist/editor/group-manager/MemberList.d.ts +1 -2
  340. package/dist/editor/group-manager/MemberList.js +0 -1
  341. package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts +1 -2
  342. package/dist/editor/group-manager/hooks/useExternalGroupSearch.js +0 -1
  343. package/dist/editor/group-manager/hooks/useGroupState.d.ts +1 -2
  344. package/dist/editor/group-manager/hooks/useGroupState.js +0 -1
  345. package/dist/editor/group-manager/hooks/useUserSearch.d.ts +1 -2
  346. package/dist/editor/group-manager/hooks/useUserSearch.js +0 -1
  347. package/dist/editor/group-manager/index.d.ts +5 -6
  348. package/dist/editor/group-manager/index.js +0 -1
  349. package/dist/editor/group-manager/types.d.ts +4 -5
  350. package/dist/editor/group-manager/types.js +0 -1
  351. package/dist/editor/hooks/index.d.ts +14 -15
  352. package/dist/editor/hooks/index.js +0 -1
  353. package/dist/editor/hooks/useBranchActions.d.ts +0 -1
  354. package/dist/editor/hooks/useBranchActions.js +0 -1
  355. package/dist/editor/hooks/useBranchManager.d.ts +4 -5
  356. package/dist/editor/hooks/useBranchManager.js +0 -1
  357. package/dist/editor/hooks/useBranchesData.d.ts +2 -3
  358. package/dist/editor/hooks/useBranchesData.js +0 -1
  359. package/dist/editor/hooks/useCommentSystem.d.ts +2 -3
  360. package/dist/editor/hooks/useCommentSystem.js +0 -1
  361. package/dist/editor/hooks/useCommentsData.d.ts +2 -3
  362. package/dist/editor/hooks/useCommentsData.js +0 -1
  363. package/dist/editor/hooks/useDraftManager.d.ts +3 -4
  364. package/dist/editor/hooks/useDraftManager.js +0 -1
  365. package/dist/editor/hooks/useEditorLayout.d.ts +1 -2
  366. package/dist/editor/hooks/useEditorLayout.js +0 -1
  367. package/dist/editor/hooks/useEntriesData.d.ts +3 -5
  368. package/dist/editor/hooks/useEntriesData.js +0 -3
  369. package/dist/editor/hooks/useEntryLinkResolution.d.ts +2 -3
  370. package/dist/editor/hooks/useEntryLinkResolution.js +0 -1
  371. package/dist/editor/hooks/useEntryManager.d.ts +5 -7
  372. package/dist/editor/hooks/useEntryManager.js +8 -3
  373. package/dist/editor/hooks/useGroupManager.d.ts +2 -3
  374. package/dist/editor/hooks/useGroupManager.js +0 -1
  375. package/dist/editor/hooks/usePermissionManager.d.ts +2 -3
  376. package/dist/editor/hooks/usePermissionManager.js +0 -1
  377. package/dist/editor/hooks/useReferenceResolution.d.ts +1 -2
  378. package/dist/editor/hooks/useReferenceResolution.js +8 -3
  379. package/dist/editor/hooks/useSchemaManager.d.ts +2 -3
  380. package/dist/editor/hooks/useSchemaManager.js +0 -1
  381. package/dist/editor/hooks/useUserContext.d.ts +1 -2
  382. package/dist/editor/hooks/useUserContext.js +8 -3
  383. package/dist/editor/hooks/useUserMetadata.d.ts +2 -3
  384. package/dist/editor/hooks/useUserMetadata.js +0 -1
  385. package/dist/editor/media/AssetCard.d.ts +1 -2
  386. package/dist/editor/media/AssetCard.js +0 -1
  387. package/dist/editor/media/CropStep.d.ts +1 -2
  388. package/dist/editor/media/CropStep.js +0 -1
  389. package/dist/editor/media/MediaLibrary.d.ts +1 -2
  390. package/dist/editor/media/MediaLibrary.js +0 -1
  391. package/dist/editor/media/MediaLibraryBody.d.ts +1 -2
  392. package/dist/editor/media/MediaLibraryBody.js +0 -1
  393. package/dist/editor/media/crop-math.d.ts +1 -2
  394. package/dist/editor/media/crop-math.js +0 -1
  395. package/dist/editor/media/upload-asset.d.ts +2 -3
  396. package/dist/editor/media/upload-asset.js +0 -1
  397. package/dist/editor/media/upload-constants.d.ts +0 -1
  398. package/dist/editor/media/upload-constants.js +0 -1
  399. package/dist/editor/media/useAssetUpload.d.ts +1 -2
  400. package/dist/editor/media/useAssetUpload.js +0 -1
  401. package/dist/editor/media/xhr-upload.d.ts +0 -1
  402. package/dist/editor/media/xhr-upload.js +0 -1
  403. package/dist/editor/permission-manager/GroupSelector.d.ts +1 -2
  404. package/dist/editor/permission-manager/GroupSelector.js +0 -1
  405. package/dist/editor/permission-manager/PermissionEditor.d.ts +1 -2
  406. package/dist/editor/permission-manager/PermissionEditor.js +0 -1
  407. package/dist/editor/permission-manager/PermissionLevelBadge.d.ts +1 -2
  408. package/dist/editor/permission-manager/PermissionLevelBadge.js +0 -1
  409. package/dist/editor/permission-manager/PermissionTree.d.ts +1 -2
  410. package/dist/editor/permission-manager/PermissionTree.js +0 -1
  411. package/dist/editor/permission-manager/UserSelector.d.ts +1 -2
  412. package/dist/editor/permission-manager/UserSelector.js +0 -1
  413. package/dist/editor/permission-manager/constants.d.ts +1 -2
  414. package/dist/editor/permission-manager/constants.js +0 -1
  415. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +1 -2
  416. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +0 -1
  417. package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts +2 -3
  418. package/dist/editor/permission-manager/hooks/usePermissionTree.js +0 -1
  419. package/dist/editor/permission-manager/index.d.ts +4 -5
  420. package/dist/editor/permission-manager/index.js +0 -1
  421. package/dist/editor/permission-manager/types.d.ts +3 -4
  422. package/dist/editor/permission-manager/types.js +0 -1
  423. package/dist/editor/permission-manager/utils.d.ts +2 -3
  424. package/dist/editor/permission-manager/utils.js +0 -1
  425. package/dist/editor/preview-bridge.d.ts +1 -2
  426. package/dist/editor/preview-bridge.js +0 -1
  427. package/dist/editor/relative-time.d.ts +0 -1
  428. package/dist/editor/relative-time.js +0 -1
  429. package/dist/editor/schema-editor/CollectionEditor.d.ts +4 -5
  430. package/dist/editor/schema-editor/CollectionEditor.js +0 -1
  431. package/dist/editor/schema-editor/EntryTypeEditor.d.ts +2 -3
  432. package/dist/editor/schema-editor/EntryTypeEditor.js +0 -1
  433. package/dist/editor/schema-editor/index.d.ts +2 -3
  434. package/dist/editor/schema-editor/index.js +0 -1
  435. package/dist/editor/theme.d.ts +0 -1
  436. package/dist/editor/theme.js +0 -1
  437. package/dist/editor/utils/env.d.ts +0 -1
  438. package/dist/editor/utils/env.js +0 -1
  439. package/dist/entry-link-resolver.d.ts +1 -2
  440. package/dist/entry-link-resolver.js +0 -1
  441. package/dist/entry-schema-registry.d.ts +3 -4
  442. package/dist/entry-schema-registry.js +6 -2
  443. package/dist/entry-schema.d.ts +114 -3
  444. package/dist/entry-schema.js +46 -1
  445. package/dist/git-manager.d.ts +1 -2
  446. package/dist/git-manager.js +0 -1
  447. package/dist/github-service.d.ts +1 -2
  448. package/dist/github-service.js +0 -1
  449. package/dist/http/handler.d.ts +6 -7
  450. package/dist/http/handler.js +0 -1
  451. package/dist/http/index.d.ts +6 -7
  452. package/dist/http/index.js +0 -1
  453. package/dist/http/router.d.ts +2 -3
  454. package/dist/http/router.js +0 -1
  455. package/dist/http/types.d.ts +0 -1
  456. package/dist/http/types.js +0 -1
  457. package/dist/id.d.ts +2 -3
  458. package/dist/id.js +0 -1
  459. package/dist/index.d.ts +17 -18
  460. package/dist/index.js +0 -1
  461. package/dist/operating-mode/client-safe-strategy.d.ts +1 -2
  462. package/dist/operating-mode/client-safe-strategy.js +0 -1
  463. package/dist/operating-mode/client-unsafe-strategy.d.ts +1 -2
  464. package/dist/operating-mode/client-unsafe-strategy.js +0 -1
  465. package/dist/operating-mode/client.d.ts +3 -4
  466. package/dist/operating-mode/client.js +0 -1
  467. package/dist/operating-mode/deployment-name-fixtures.d.ts +0 -1
  468. package/dist/operating-mode/deployment-name-fixtures.js +0 -1
  469. package/dist/operating-mode/deployment-name.d.ts +0 -1
  470. package/dist/operating-mode/deployment-name.js +0 -1
  471. package/dist/operating-mode/index.d.ts +5 -6
  472. package/dist/operating-mode/index.js +0 -1
  473. package/dist/operating-mode/mode-env.d.ts +1 -2
  474. package/dist/operating-mode/mode-env.js +0 -1
  475. package/dist/operating-mode/types.d.ts +2 -3
  476. package/dist/operating-mode/types.js +0 -1
  477. package/dist/paths/branch-name.d.ts +1 -2
  478. package/dist/paths/branch-name.js +0 -1
  479. package/dist/paths/branch.d.ts +3 -4
  480. package/dist/paths/branch.js +0 -1
  481. package/dist/paths/index.d.ts +8 -9
  482. package/dist/paths/index.js +0 -1
  483. package/dist/paths/normalize-server.d.ts +1 -2
  484. package/dist/paths/normalize-server.js +0 -1
  485. package/dist/paths/normalize.d.ts +1 -2
  486. package/dist/paths/normalize.js +0 -1
  487. package/dist/paths/resolve.d.ts +2 -3
  488. package/dist/paths/resolve.js +0 -1
  489. package/dist/paths/types.d.ts +0 -1
  490. package/dist/paths/types.js +0 -1
  491. package/dist/paths/validation.d.ts +1 -2
  492. package/dist/paths/validation.js +0 -1
  493. package/dist/reference-resolver.d.ts +3 -4
  494. package/dist/reference-resolver.js +0 -1
  495. package/dist/resolve-canopy-user.d.ts +3 -4
  496. package/dist/resolve-canopy-user.js +0 -1
  497. package/dist/resource-generation.d.ts +0 -1
  498. package/dist/resource-generation.js +0 -1
  499. package/dist/schema/index.d.ts +3 -4
  500. package/dist/schema/index.js +0 -1
  501. package/dist/schema/meta-loader.d.ts +3 -4
  502. package/dist/schema/meta-loader.js +3 -3
  503. package/dist/schema/resolver.d.ts +2 -3
  504. package/dist/schema/resolver.js +0 -1
  505. package/dist/schema/schema-store-types.d.ts +2 -3
  506. package/dist/schema/schema-store-types.js +0 -1
  507. package/dist/schema/schema-store.d.ts +6 -7
  508. package/dist/schema/schema-store.js +33 -1
  509. package/dist/schema/types.d.ts +1 -2
  510. package/dist/schema/types.js +0 -1
  511. package/dist/server.d.ts +54 -45
  512. package/dist/server.js +11 -3
  513. package/dist/services.d.ts +10 -11
  514. package/dist/services.js +0 -1
  515. package/dist/settings-workspace.d.ts +12 -15
  516. package/dist/settings-workspace.js +10 -13
  517. package/dist/static/index.d.ts +164 -6
  518. package/dist/static/index.js +242 -7
  519. package/dist/static/seo.d.ts +8 -30
  520. package/dist/static/seo.js +19 -46
  521. package/dist/sync-core.d.ts +0 -1
  522. package/dist/sync-core.js +0 -1
  523. package/dist/task-queue/index.d.ts +2 -3
  524. package/dist/task-queue/index.js +0 -1
  525. package/dist/task-queue/task-queue.d.ts +1 -2
  526. package/dist/task-queue/task-queue.js +10 -2
  527. package/dist/task-queue/types.d.ts +0 -1
  528. package/dist/task-queue/types.js +0 -1
  529. package/dist/types.d.ts +3 -4
  530. package/dist/types.js +0 -1
  531. package/dist/url-collision.d.ts +86 -0
  532. package/dist/url-collision.js +186 -0
  533. package/dist/url-path-resolver.d.ts +11 -2
  534. package/dist/url-path-resolver.js +37 -5
  535. package/dist/user.d.ts +3 -4
  536. package/dist/user.js +0 -1
  537. package/dist/utils/async-mutex.d.ts +0 -1
  538. package/dist/utils/async-mutex.js +0 -1
  539. package/dist/utils/atomic-write.d.ts +0 -1
  540. package/dist/utils/atomic-write.js +0 -1
  541. package/dist/utils/body-field.d.ts +12 -2
  542. package/dist/utils/body-field.js +17 -1
  543. package/dist/utils/content-serialize.d.ts +37 -0
  544. package/dist/utils/content-serialize.js +429 -0
  545. package/dist/utils/content-write-lock.d.ts +12 -16
  546. package/dist/utils/content-write-lock.js +38 -22
  547. package/dist/utils/debug.d.ts +0 -1
  548. package/dist/utils/debug.js +0 -1
  549. package/dist/utils/entry-url.d.ts +17 -1
  550. package/dist/utils/entry-url.js +21 -3
  551. package/dist/utils/error.d.ts +0 -1
  552. package/dist/utils/error.js +0 -1
  553. package/dist/utils/flatten-group-fields.d.ts +1 -2
  554. package/dist/utils/flatten-group-fields.js +0 -1
  555. package/dist/utils/format.d.ts +1 -2
  556. package/dist/utils/format.js +0 -1
  557. package/dist/utils/fs.d.ts +7 -1
  558. package/dist/utils/fs.js +16 -1
  559. package/dist/utils/git.d.ts +18 -2
  560. package/dist/utils/git.js +61 -1
  561. package/dist/utils/logger.d.ts +0 -1
  562. package/dist/utils/logger.js +0 -1
  563. package/dist/utils/occ-json-write.d.ts +0 -1
  564. package/dist/utils/occ-json-write.js +0 -1
  565. package/dist/utils/provisioning-lock.d.ts +17 -4
  566. package/dist/utils/provisioning-lock.js +94 -14
  567. package/dist/utils/sanitize-href.d.ts +2 -3
  568. package/dist/utils/sanitize-href.js +2 -3
  569. package/dist/utils/title-field.d.ts +1 -2
  570. package/dist/utils/title-field.js +0 -1
  571. package/dist/utils/typed-filename.d.ts +61 -0
  572. package/dist/utils/typed-filename.js +92 -0
  573. package/dist/utils/url-prefix.d.ts +121 -0
  574. package/dist/utils/url-prefix.js +158 -0
  575. package/dist/validation/block-structural-keys.d.ts +42 -0
  576. package/dist/validation/block-structural-keys.js +44 -0
  577. package/dist/validation/deletion-checker.d.ts +4 -5
  578. package/dist/validation/deletion-checker.js +0 -1
  579. package/dist/validation/entry-link-validator.d.ts +2 -3
  580. package/dist/validation/entry-link-validator.js +0 -1
  581. package/dist/validation/entry-type-reference-validator.d.ts +1 -2
  582. package/dist/validation/entry-type-reference-validator.js +0 -1
  583. package/dist/validation/entry-validator.d.ts +23 -2
  584. package/dist/validation/entry-validator.js +34 -2
  585. package/dist/validation/field-traversal.d.ts +28 -4
  586. package/dist/validation/field-traversal.js +24 -7
  587. package/dist/validation/reference-validator.d.ts +2 -3
  588. package/dist/validation/reference-validator.js +0 -1
  589. package/dist/worker/cms-worker.d.ts +31 -2
  590. package/dist/worker/cms-worker.js +321 -13
  591. package/dist/worker/log.d.ts +0 -1
  592. package/dist/worker/log.js +0 -1
  593. package/dist/worker/task-queue-config.d.ts +1 -2
  594. package/dist/worker/task-queue-config.js +0 -1
  595. package/dist/worker/task-queue.d.ts +3 -4
  596. package/dist/worker/task-queue.js +0 -1
  597. package/dist/worker/worker-status.d.ts +1 -2
  598. package/dist/worker/worker-status.js +0 -1
  599. package/package.json +3 -6
  600. package/dist/ai/generate.d.ts.map +0 -1
  601. package/dist/ai/generate.js.map +0 -1
  602. package/dist/ai/handler.d.ts.map +0 -1
  603. package/dist/ai/handler.js.map +0 -1
  604. package/dist/ai/index.d.ts.map +0 -1
  605. package/dist/ai/index.js.map +0 -1
  606. package/dist/ai/json-to-markdown.d.ts.map +0 -1
  607. package/dist/ai/json-to-markdown.js.map +0 -1
  608. package/dist/ai/resolve-branch.d.ts.map +0 -1
  609. package/dist/ai/resolve-branch.js.map +0 -1
  610. package/dist/ai/strip-mdx.d.ts.map +0 -1
  611. package/dist/ai/strip-mdx.js.map +0 -1
  612. package/dist/ai/to-plain-text.d.ts.map +0 -1
  613. package/dist/ai/to-plain-text.js.map +0 -1
  614. package/dist/ai/transform-components.d.ts.map +0 -1
  615. package/dist/ai/transform-components.js.map +0 -1
  616. package/dist/ai/types.d.ts.map +0 -1
  617. package/dist/ai/types.js.map +0 -1
  618. package/dist/api/__test__/mock-client.d.ts +0 -231
  619. package/dist/api/__test__/mock-client.d.ts.map +0 -1
  620. package/dist/api/__test__/mock-client.js +0 -388
  621. package/dist/api/__test__/mock-client.js.map +0 -1
  622. package/dist/api/admin-branch-health.d.ts.map +0 -1
  623. package/dist/api/admin-branch-health.js.map +0 -1
  624. package/dist/api/admin.d.ts.map +0 -1
  625. package/dist/api/admin.js.map +0 -1
  626. package/dist/api/assets.d.ts.map +0 -1
  627. package/dist/api/assets.js.map +0 -1
  628. package/dist/api/branch-merge.d.ts.map +0 -1
  629. package/dist/api/branch-merge.js.map +0 -1
  630. package/dist/api/branch-review.d.ts.map +0 -1
  631. package/dist/api/branch-review.js.map +0 -1
  632. package/dist/api/branch-status.d.ts.map +0 -1
  633. package/dist/api/branch-status.js.map +0 -1
  634. package/dist/api/branch-withdraw.d.ts.map +0 -1
  635. package/dist/api/branch-withdraw.js.map +0 -1
  636. package/dist/api/branch.d.ts.map +0 -1
  637. package/dist/api/branch.js.map +0 -1
  638. package/dist/api/client.d.ts.map +0 -1
  639. package/dist/api/client.js.map +0 -1
  640. package/dist/api/comments.d.ts.map +0 -1
  641. package/dist/api/comments.js.map +0 -1
  642. package/dist/api/content.d.ts.map +0 -1
  643. package/dist/api/content.js.map +0 -1
  644. package/dist/api/entries-constants.d.ts.map +0 -1
  645. package/dist/api/entries-constants.js.map +0 -1
  646. package/dist/api/entries.d.ts.map +0 -1
  647. package/dist/api/entries.js.map +0 -1
  648. package/dist/api/github-sync.d.ts.map +0 -1
  649. package/dist/api/github-sync.js.map +0 -1
  650. package/dist/api/groups.d.ts.map +0 -1
  651. package/dist/api/groups.js.map +0 -1
  652. package/dist/api/guards.d.ts.map +0 -1
  653. package/dist/api/guards.js.map +0 -1
  654. package/dist/api/index.d.ts.map +0 -1
  655. package/dist/api/index.js.map +0 -1
  656. package/dist/api/permissions.d.ts.map +0 -1
  657. package/dist/api/permissions.js.map +0 -1
  658. package/dist/api/reference-options.d.ts.map +0 -1
  659. package/dist/api/reference-options.js.map +0 -1
  660. package/dist/api/request-body-hash.d.ts.map +0 -1
  661. package/dist/api/request-body-hash.js.map +0 -1
  662. package/dist/api/resolve-references.d.ts.map +0 -1
  663. package/dist/api/resolve-references.js.map +0 -1
  664. package/dist/api/route-builder.d.ts.map +0 -1
  665. package/dist/api/route-builder.js.map +0 -1
  666. package/dist/api/schema.d.ts.map +0 -1
  667. package/dist/api/schema.js.map +0 -1
  668. package/dist/api/settings-helpers.d.ts.map +0 -1
  669. package/dist/api/settings-helpers.js.map +0 -1
  670. package/dist/api/types.d.ts.map +0 -1
  671. package/dist/api/types.js.map +0 -1
  672. package/dist/api/user.d.ts.map +0 -1
  673. package/dist/api/user.js.map +0 -1
  674. package/dist/api/validators.d.ts.map +0 -1
  675. package/dist/api/validators.js.map +0 -1
  676. package/dist/assets/asset-prefixes.d.ts.map +0 -1
  677. package/dist/assets/asset-prefixes.js.map +0 -1
  678. package/dist/assets/asset-src.d.ts.map +0 -1
  679. package/dist/assets/asset-src.js.map +0 -1
  680. package/dist/assets/asset-url.d.ts.map +0 -1
  681. package/dist/assets/asset-url.js.map +0 -1
  682. package/dist/assets/factory.d.ts.map +0 -1
  683. package/dist/assets/factory.js.map +0 -1
  684. package/dist/assets/finalize.d.ts.map +0 -1
  685. package/dist/assets/finalize.js.map +0 -1
  686. package/dist/assets/index.d.ts.map +0 -1
  687. package/dist/assets/index.js.map +0 -1
  688. package/dist/assets/keys.d.ts.map +0 -1
  689. package/dist/assets/keys.js.map +0 -1
  690. package/dist/assets/pipeline.d.ts.map +0 -1
  691. package/dist/assets/pipeline.js.map +0 -1
  692. package/dist/assets/store-local.d.ts.map +0 -1
  693. package/dist/assets/store-local.js.map +0 -1
  694. package/dist/assets/store-s3.d.ts.map +0 -1
  695. package/dist/assets/store-s3.js.map +0 -1
  696. package/dist/assets/svg-sanitizer.d.ts.map +0 -1
  697. package/dist/assets/svg-sanitizer.js.map +0 -1
  698. package/dist/assets/transform-directives.d.ts.map +0 -1
  699. package/dist/assets/transform-directives.js.map +0 -1
  700. package/dist/assets/transform.d.ts.map +0 -1
  701. package/dist/assets/transform.js.map +0 -1
  702. package/dist/assets/types.d.ts.map +0 -1
  703. package/dist/assets/types.js.map +0 -1
  704. package/dist/auth/cache.d.ts.map +0 -1
  705. package/dist/auth/cache.js.map +0 -1
  706. package/dist/auth/caching-auth-plugin.d.ts.map +0 -1
  707. package/dist/auth/caching-auth-plugin.js.map +0 -1
  708. package/dist/auth/context-helpers.d.ts.map +0 -1
  709. package/dist/auth/context-helpers.js.map +0 -1
  710. package/dist/auth/file-based-auth-cache.d.ts.map +0 -1
  711. package/dist/auth/file-based-auth-cache.js.map +0 -1
  712. package/dist/auth/index.d.ts.map +0 -1
  713. package/dist/auth/index.js.map +0 -1
  714. package/dist/auth/plugin.d.ts.map +0 -1
  715. package/dist/auth/plugin.js.map +0 -1
  716. package/dist/auth/types.d.ts.map +0 -1
  717. package/dist/auth/types.js.map +0 -1
  718. package/dist/authorization/branch.d.ts.map +0 -1
  719. package/dist/authorization/branch.js.map +0 -1
  720. package/dist/authorization/content.d.ts.map +0 -1
  721. package/dist/authorization/content.js.map +0 -1
  722. package/dist/authorization/groups/index.d.ts.map +0 -1
  723. package/dist/authorization/groups/index.js.map +0 -1
  724. package/dist/authorization/groups/loader.d.ts.map +0 -1
  725. package/dist/authorization/groups/loader.js.map +0 -1
  726. package/dist/authorization/groups/schema.d.ts.map +0 -1
  727. package/dist/authorization/groups/schema.js.map +0 -1
  728. package/dist/authorization/helpers.d.ts.map +0 -1
  729. package/dist/authorization/helpers.js.map +0 -1
  730. package/dist/authorization/index.d.ts.map +0 -1
  731. package/dist/authorization/index.js.map +0 -1
  732. package/dist/authorization/path.d.ts.map +0 -1
  733. package/dist/authorization/path.js.map +0 -1
  734. package/dist/authorization/permissions/index.d.ts.map +0 -1
  735. package/dist/authorization/permissions/index.js.map +0 -1
  736. package/dist/authorization/permissions/loader.d.ts.map +0 -1
  737. package/dist/authorization/permissions/loader.js.map +0 -1
  738. package/dist/authorization/permissions/schema.d.ts.map +0 -1
  739. package/dist/authorization/permissions/schema.js.map +0 -1
  740. package/dist/authorization/protected-branch.d.ts.map +0 -1
  741. package/dist/authorization/protected-branch.js.map +0 -1
  742. package/dist/authorization/settings-file-store.d.ts.map +0 -1
  743. package/dist/authorization/settings-file-store.js.map +0 -1
  744. package/dist/authorization/test-utils.d.ts +0 -26
  745. package/dist/authorization/test-utils.d.ts.map +0 -1
  746. package/dist/authorization/test-utils.js +0 -23
  747. package/dist/authorization/test-utils.js.map +0 -1
  748. package/dist/authorization/types.d.ts.map +0 -1
  749. package/dist/authorization/types.js.map +0 -1
  750. package/dist/authorization/validation.d.ts.map +0 -1
  751. package/dist/authorization/validation.js.map +0 -1
  752. package/dist/branch-health.d.ts.map +0 -1
  753. package/dist/branch-health.js.map +0 -1
  754. package/dist/branch-metadata.d.ts.map +0 -1
  755. package/dist/branch-metadata.js.map +0 -1
  756. package/dist/branch-registry.d.ts.map +0 -1
  757. package/dist/branch-registry.js.map +0 -1
  758. package/dist/branch-schema-cache.d.ts.map +0 -1
  759. package/dist/branch-schema-cache.js.map +0 -1
  760. package/dist/branch-workspace.d.ts.map +0 -1
  761. package/dist/branch-workspace.js.map +0 -1
  762. package/dist/build/generate-ai-content.d.ts.map +0 -1
  763. package/dist/build/generate-ai-content.js.map +0 -1
  764. package/dist/build/index.d.ts.map +0 -1
  765. package/dist/build/index.js.map +0 -1
  766. package/dist/build-canopy.d.ts.map +0 -1
  767. package/dist/build-canopy.js.map +0 -1
  768. package/dist/build-mode.d.ts.map +0 -1
  769. package/dist/build-mode.js.map +0 -1
  770. package/dist/cli/cli.d.ts.map +0 -1
  771. package/dist/cli/cli.js.map +0 -1
  772. package/dist/cli/generate-ai-content.d.ts.map +0 -1
  773. package/dist/cli/generate-ai-content.js.map +0 -1
  774. package/dist/cli/init.d.ts.map +0 -1
  775. package/dist/cli/init.js.map +0 -1
  776. package/dist/cli/migrate.d.ts.map +0 -1
  777. package/dist/cli/migrate.js.map +0 -1
  778. package/dist/cli/project-detect.d.ts.map +0 -1
  779. package/dist/cli/project-detect.js.map +0 -1
  780. package/dist/cli/project-root.d.ts.map +0 -1
  781. package/dist/cli/project-root.js.map +0 -1
  782. package/dist/cli/sync.d.ts.map +0 -1
  783. package/dist/cli/sync.js.map +0 -1
  784. package/dist/cli/templates.d.ts.map +0 -1
  785. package/dist/cli/templates.js.map +0 -1
  786. package/dist/client.d.ts.map +0 -1
  787. package/dist/client.js.map +0 -1
  788. package/dist/comment-store.d.ts.map +0 -1
  789. package/dist/comment-store.js.map +0 -1
  790. package/dist/config/flatten.d.ts.map +0 -1
  791. package/dist/config/flatten.js.map +0 -1
  792. package/dist/config/helpers.d.ts.map +0 -1
  793. package/dist/config/helpers.js.map +0 -1
  794. package/dist/config/index.d.ts.map +0 -1
  795. package/dist/config/index.js.map +0 -1
  796. package/dist/config/schemas/collection.d.ts.map +0 -1
  797. package/dist/config/schemas/collection.js.map +0 -1
  798. package/dist/config/schemas/config.d.ts.map +0 -1
  799. package/dist/config/schemas/config.js.map +0 -1
  800. package/dist/config/schemas/field.d.ts.map +0 -1
  801. package/dist/config/schemas/field.js.map +0 -1
  802. package/dist/config/schemas/media.d.ts.map +0 -1
  803. package/dist/config/schemas/media.js.map +0 -1
  804. package/dist/config/schemas/permissions.d.ts.map +0 -1
  805. package/dist/config/schemas/permissions.js.map +0 -1
  806. package/dist/config/types.d.ts.map +0 -1
  807. package/dist/config/types.js.map +0 -1
  808. package/dist/config/validation.d.ts.map +0 -1
  809. package/dist/config/validation.js.map +0 -1
  810. package/dist/config-test.d.ts +0 -20
  811. package/dist/config-test.d.ts.map +0 -1
  812. package/dist/config-test.js +0 -49
  813. package/dist/config-test.js.map +0 -1
  814. package/dist/config.d.ts.map +0 -1
  815. package/dist/config.js.map +0 -1
  816. package/dist/content-id-index.d.ts.map +0 -1
  817. package/dist/content-id-index.js.map +0 -1
  818. package/dist/content-index-generation.d.ts.map +0 -1
  819. package/dist/content-index-generation.js.map +0 -1
  820. package/dist/content-index-registry.d.ts.map +0 -1
  821. package/dist/content-index-registry.js.map +0 -1
  822. package/dist/content-listing.d.ts.map +0 -1
  823. package/dist/content-listing.js.map +0 -1
  824. package/dist/content-reader.d.ts.map +0 -1
  825. package/dist/content-reader.js.map +0 -1
  826. package/dist/content-store.d.ts.map +0 -1
  827. package/dist/content-store.js.map +0 -1
  828. package/dist/content-tree.d.ts.map +0 -1
  829. package/dist/content-tree.js.map +0 -1
  830. package/dist/context.d.ts.map +0 -1
  831. package/dist/context.js.map +0 -1
  832. package/dist/dev-content-watcher.d.ts.map +0 -1
  833. package/dist/dev-content-watcher.js.map +0 -1
  834. package/dist/editor/BranchManager.d.ts.map +0 -1
  835. package/dist/editor/BranchManager.js.map +0 -1
  836. package/dist/editor/CanopyEditor.d.ts.map +0 -1
  837. package/dist/editor/CanopyEditor.js.map +0 -1
  838. package/dist/editor/CanopyEditorPage.d.ts.map +0 -1
  839. package/dist/editor/CanopyEditorPage.js.map +0 -1
  840. package/dist/editor/CommentsPanel.d.ts.map +0 -1
  841. package/dist/editor/CommentsPanel.js.map +0 -1
  842. package/dist/editor/Editor.d.ts.map +0 -1
  843. package/dist/editor/Editor.js.map +0 -1
  844. package/dist/editor/EditorPanes.d.ts.map +0 -1
  845. package/dist/editor/EditorPanes.js.map +0 -1
  846. package/dist/editor/EntryNavigator.d.ts.map +0 -1
  847. package/dist/editor/EntryNavigator.js.map +0 -1
  848. package/dist/editor/FormRenderer.d.ts.map +0 -1
  849. package/dist/editor/FormRenderer.js.map +0 -1
  850. package/dist/editor/GroupManager.d.ts.map +0 -1
  851. package/dist/editor/GroupManager.js.map +0 -1
  852. package/dist/editor/PermissionManager.d.ts.map +0 -1
  853. package/dist/editor/PermissionManager.js.map +0 -1
  854. package/dist/editor/admin/SystemHealthPanel.d.ts.map +0 -1
  855. package/dist/editor/admin/SystemHealthPanel.js.map +0 -1
  856. package/dist/editor/admin/useSystemHealth.d.ts.map +0 -1
  857. package/dist/editor/admin/useSystemHealth.js.map +0 -1
  858. package/dist/editor/canopy-path.d.ts.map +0 -1
  859. package/dist/editor/canopy-path.js.map +0 -1
  860. package/dist/editor/client-reference-resolver.d.ts.map +0 -1
  861. package/dist/editor/client-reference-resolver.js.map +0 -1
  862. package/dist/editor/comments/BranchComments.d.ts.map +0 -1
  863. package/dist/editor/comments/BranchComments.js.map +0 -1
  864. package/dist/editor/comments/EntryComments.d.ts.map +0 -1
  865. package/dist/editor/comments/EntryComments.js.map +0 -1
  866. package/dist/editor/comments/FieldWrapper.d.ts.map +0 -1
  867. package/dist/editor/comments/FieldWrapper.js.map +0 -1
  868. package/dist/editor/comments/InlineCommentThread.d.ts.map +0 -1
  869. package/dist/editor/comments/InlineCommentThread.js.map +0 -1
  870. package/dist/editor/comments/ThreadCarousel.d.ts.map +0 -1
  871. package/dist/editor/comments/ThreadCarousel.js.map +0 -1
  872. package/dist/editor/components/ConfirmDeleteModal.d.ts.map +0 -1
  873. package/dist/editor/components/ConfirmDeleteModal.js.map +0 -1
  874. package/dist/editor/components/EditorFooter.d.ts.map +0 -1
  875. package/dist/editor/components/EditorFooter.js.map +0 -1
  876. package/dist/editor/components/EditorHeader.d.ts.map +0 -1
  877. package/dist/editor/components/EditorHeader.js.map +0 -1
  878. package/dist/editor/components/EditorSidebar.d.ts.map +0 -1
  879. package/dist/editor/components/EditorSidebar.js.map +0 -1
  880. package/dist/editor/components/EntryCreateModal.d.ts.map +0 -1
  881. package/dist/editor/components/EntryCreateModal.js.map +0 -1
  882. package/dist/editor/components/RenameEntryModal.d.ts.map +0 -1
  883. package/dist/editor/components/RenameEntryModal.js.map +0 -1
  884. package/dist/editor/components/UserBadge.d.ts.map +0 -1
  885. package/dist/editor/components/UserBadge.js.map +0 -1
  886. package/dist/editor/components/index.d.ts.map +0 -1
  887. package/dist/editor/components/index.js.map +0 -1
  888. package/dist/editor/context/ApiClientContext.d.ts.map +0 -1
  889. package/dist/editor/context/ApiClientContext.js.map +0 -1
  890. package/dist/editor/context/AssetContext.d.ts.map +0 -1
  891. package/dist/editor/context/AssetContext.js.map +0 -1
  892. package/dist/editor/context/EditorStateContext.d.ts.map +0 -1
  893. package/dist/editor/context/EditorStateContext.js.map +0 -1
  894. package/dist/editor/context/SWRProvider.d.ts.map +0 -1
  895. package/dist/editor/context/SWRProvider.js.map +0 -1
  896. package/dist/editor/context/index.d.ts.map +0 -1
  897. package/dist/editor/context/index.js.map +0 -1
  898. package/dist/editor/editor-config.d.ts.map +0 -1
  899. package/dist/editor/editor-config.js.map +0 -1
  900. package/dist/editor/editor-utils.d.ts.map +0 -1
  901. package/dist/editor/editor-utils.js.map +0 -1
  902. package/dist/editor/fields/BlockField.d.ts.map +0 -1
  903. package/dist/editor/fields/BlockField.js.map +0 -1
  904. package/dist/editor/fields/CodeField.d.ts.map +0 -1
  905. package/dist/editor/fields/CodeField.js.map +0 -1
  906. package/dist/editor/fields/DateTimeField.d.ts.map +0 -1
  907. package/dist/editor/fields/DateTimeField.js.map +0 -1
  908. package/dist/editor/fields/ImageField.d.ts.map +0 -1
  909. package/dist/editor/fields/ImageField.js.map +0 -1
  910. package/dist/editor/fields/InlineGroupField.d.ts.map +0 -1
  911. package/dist/editor/fields/InlineGroupField.js.map +0 -1
  912. package/dist/editor/fields/MarkdownField.d.ts.map +0 -1
  913. package/dist/editor/fields/MarkdownField.js.map +0 -1
  914. package/dist/editor/fields/MdxImageDialog.d.ts.map +0 -1
  915. package/dist/editor/fields/MdxImageDialog.js.map +0 -1
  916. package/dist/editor/fields/NumberField.d.ts.map +0 -1
  917. package/dist/editor/fields/NumberField.js.map +0 -1
  918. package/dist/editor/fields/NumberListField.d.ts.map +0 -1
  919. package/dist/editor/fields/NumberListField.js.map +0 -1
  920. package/dist/editor/fields/ObjectField.d.ts.map +0 -1
  921. package/dist/editor/fields/ObjectField.js.map +0 -1
  922. package/dist/editor/fields/ReferenceField.d.ts.map +0 -1
  923. package/dist/editor/fields/ReferenceField.js.map +0 -1
  924. package/dist/editor/fields/SelectField.d.ts.map +0 -1
  925. package/dist/editor/fields/SelectField.js.map +0 -1
  926. package/dist/editor/fields/StringListField.d.ts.map +0 -1
  927. package/dist/editor/fields/StringListField.js.map +0 -1
  928. package/dist/editor/fields/TextField.d.ts.map +0 -1
  929. package/dist/editor/fields/TextField.js.map +0 -1
  930. package/dist/editor/fields/ToggleField.d.ts.map +0 -1
  931. package/dist/editor/fields/ToggleField.js.map +0 -1
  932. package/dist/editor/fields/entry-link/EntryLinkContext.d.ts.map +0 -1
  933. package/dist/editor/fields/entry-link/EntryLinkContext.js.map +0 -1
  934. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts.map +0 -1
  935. package/dist/editor/fields/entry-link/InsertEntryLink.js.map +0 -1
  936. package/dist/editor/fields/entry-link/index.d.ts.map +0 -1
  937. package/dist/editor/fields/entry-link/index.js.map +0 -1
  938. package/dist/editor/group-manager/ExternalGroupsTab.d.ts.map +0 -1
  939. package/dist/editor/group-manager/ExternalGroupsTab.js.map +0 -1
  940. package/dist/editor/group-manager/GroupCard.d.ts.map +0 -1
  941. package/dist/editor/group-manager/GroupCard.js.map +0 -1
  942. package/dist/editor/group-manager/GroupForm.d.ts.map +0 -1
  943. package/dist/editor/group-manager/GroupForm.js.map +0 -1
  944. package/dist/editor/group-manager/InternalGroupsTab.d.ts.map +0 -1
  945. package/dist/editor/group-manager/InternalGroupsTab.js.map +0 -1
  946. package/dist/editor/group-manager/MemberList.d.ts.map +0 -1
  947. package/dist/editor/group-manager/MemberList.js.map +0 -1
  948. package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts.map +0 -1
  949. package/dist/editor/group-manager/hooks/useExternalGroupSearch.js.map +0 -1
  950. package/dist/editor/group-manager/hooks/useGroupState.d.ts.map +0 -1
  951. package/dist/editor/group-manager/hooks/useGroupState.js.map +0 -1
  952. package/dist/editor/group-manager/hooks/useUserSearch.d.ts.map +0 -1
  953. package/dist/editor/group-manager/hooks/useUserSearch.js.map +0 -1
  954. package/dist/editor/group-manager/index.d.ts.map +0 -1
  955. package/dist/editor/group-manager/index.js.map +0 -1
  956. package/dist/editor/group-manager/types.d.ts.map +0 -1
  957. package/dist/editor/group-manager/types.js.map +0 -1
  958. package/dist/editor/hooks/__test__/test-utils.d.ts +0 -164
  959. package/dist/editor/hooks/__test__/test-utils.d.ts.map +0 -1
  960. package/dist/editor/hooks/__test__/test-utils.js +0 -203
  961. package/dist/editor/hooks/__test__/test-utils.js.map +0 -1
  962. package/dist/editor/hooks/index.d.ts.map +0 -1
  963. package/dist/editor/hooks/index.js.map +0 -1
  964. package/dist/editor/hooks/useBranchActions.d.ts.map +0 -1
  965. package/dist/editor/hooks/useBranchActions.js.map +0 -1
  966. package/dist/editor/hooks/useBranchManager.d.ts.map +0 -1
  967. package/dist/editor/hooks/useBranchManager.js.map +0 -1
  968. package/dist/editor/hooks/useBranchesData.d.ts.map +0 -1
  969. package/dist/editor/hooks/useBranchesData.js.map +0 -1
  970. package/dist/editor/hooks/useCommentSystem.d.ts.map +0 -1
  971. package/dist/editor/hooks/useCommentSystem.js.map +0 -1
  972. package/dist/editor/hooks/useCommentsData.d.ts.map +0 -1
  973. package/dist/editor/hooks/useCommentsData.js.map +0 -1
  974. package/dist/editor/hooks/useDraftManager.d.ts.map +0 -1
  975. package/dist/editor/hooks/useDraftManager.js.map +0 -1
  976. package/dist/editor/hooks/useEditorLayout.d.ts.map +0 -1
  977. package/dist/editor/hooks/useEditorLayout.js.map +0 -1
  978. package/dist/editor/hooks/useEntriesData.d.ts.map +0 -1
  979. package/dist/editor/hooks/useEntriesData.js.map +0 -1
  980. package/dist/editor/hooks/useEntryLinkResolution.d.ts.map +0 -1
  981. package/dist/editor/hooks/useEntryLinkResolution.js.map +0 -1
  982. package/dist/editor/hooks/useEntryManager.d.ts.map +0 -1
  983. package/dist/editor/hooks/useEntryManager.js.map +0 -1
  984. package/dist/editor/hooks/useGroupManager.d.ts.map +0 -1
  985. package/dist/editor/hooks/useGroupManager.js.map +0 -1
  986. package/dist/editor/hooks/usePermissionManager.d.ts.map +0 -1
  987. package/dist/editor/hooks/usePermissionManager.js.map +0 -1
  988. package/dist/editor/hooks/useReferenceResolution.d.ts.map +0 -1
  989. package/dist/editor/hooks/useReferenceResolution.js.map +0 -1
  990. package/dist/editor/hooks/useSchemaManager.d.ts.map +0 -1
  991. package/dist/editor/hooks/useSchemaManager.js.map +0 -1
  992. package/dist/editor/hooks/useUserContext.d.ts.map +0 -1
  993. package/dist/editor/hooks/useUserContext.js.map +0 -1
  994. package/dist/editor/hooks/useUserMetadata.d.ts.map +0 -1
  995. package/dist/editor/hooks/useUserMetadata.js.map +0 -1
  996. package/dist/editor/media/AssetCard.d.ts.map +0 -1
  997. package/dist/editor/media/AssetCard.js.map +0 -1
  998. package/dist/editor/media/CropStep.d.ts.map +0 -1
  999. package/dist/editor/media/CropStep.js.map +0 -1
  1000. package/dist/editor/media/MediaLibrary.d.ts.map +0 -1
  1001. package/dist/editor/media/MediaLibrary.js.map +0 -1
  1002. package/dist/editor/media/MediaLibraryBody.d.ts.map +0 -1
  1003. package/dist/editor/media/MediaLibraryBody.js.map +0 -1
  1004. package/dist/editor/media/crop-math.d.ts.map +0 -1
  1005. package/dist/editor/media/crop-math.js.map +0 -1
  1006. package/dist/editor/media/upload-asset.d.ts.map +0 -1
  1007. package/dist/editor/media/upload-asset.js.map +0 -1
  1008. package/dist/editor/media/upload-constants.d.ts.map +0 -1
  1009. package/dist/editor/media/upload-constants.js.map +0 -1
  1010. package/dist/editor/media/useAssetUpload.d.ts.map +0 -1
  1011. package/dist/editor/media/useAssetUpload.js.map +0 -1
  1012. package/dist/editor/media/xhr-upload.d.ts.map +0 -1
  1013. package/dist/editor/media/xhr-upload.js.map +0 -1
  1014. package/dist/editor/permission-manager/GroupSelector.d.ts.map +0 -1
  1015. package/dist/editor/permission-manager/GroupSelector.js.map +0 -1
  1016. package/dist/editor/permission-manager/PermissionEditor.d.ts.map +0 -1
  1017. package/dist/editor/permission-manager/PermissionEditor.js.map +0 -1
  1018. package/dist/editor/permission-manager/PermissionLevelBadge.d.ts.map +0 -1
  1019. package/dist/editor/permission-manager/PermissionLevelBadge.js.map +0 -1
  1020. package/dist/editor/permission-manager/PermissionTree.d.ts.map +0 -1
  1021. package/dist/editor/permission-manager/PermissionTree.js.map +0 -1
  1022. package/dist/editor/permission-manager/UserSelector.d.ts.map +0 -1
  1023. package/dist/editor/permission-manager/UserSelector.js.map +0 -1
  1024. package/dist/editor/permission-manager/constants.d.ts.map +0 -1
  1025. package/dist/editor/permission-manager/constants.js.map +0 -1
  1026. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts.map +0 -1
  1027. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js.map +0 -1
  1028. package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts.map +0 -1
  1029. package/dist/editor/permission-manager/hooks/usePermissionTree.js.map +0 -1
  1030. package/dist/editor/permission-manager/index.d.ts.map +0 -1
  1031. package/dist/editor/permission-manager/index.js.map +0 -1
  1032. package/dist/editor/permission-manager/types.d.ts.map +0 -1
  1033. package/dist/editor/permission-manager/types.js.map +0 -1
  1034. package/dist/editor/permission-manager/utils.d.ts.map +0 -1
  1035. package/dist/editor/permission-manager/utils.js.map +0 -1
  1036. package/dist/editor/preview-bridge.d.ts.map +0 -1
  1037. package/dist/editor/preview-bridge.js.map +0 -1
  1038. package/dist/editor/relative-time.d.ts.map +0 -1
  1039. package/dist/editor/relative-time.js.map +0 -1
  1040. package/dist/editor/schema-editor/CollectionEditor.d.ts.map +0 -1
  1041. package/dist/editor/schema-editor/CollectionEditor.js.map +0 -1
  1042. package/dist/editor/schema-editor/EntryTypeEditor.d.ts.map +0 -1
  1043. package/dist/editor/schema-editor/EntryTypeEditor.js.map +0 -1
  1044. package/dist/editor/schema-editor/index.d.ts.map +0 -1
  1045. package/dist/editor/schema-editor/index.js.map +0 -1
  1046. package/dist/editor/setup-test-dom.d.ts +0 -1
  1047. package/dist/editor/setup-test-dom.d.ts.map +0 -1
  1048. package/dist/editor/setup-test-dom.js +0 -12
  1049. package/dist/editor/setup-test-dom.js.map +0 -1
  1050. package/dist/editor/test-setup.d.ts +0 -6
  1051. package/dist/editor/test-setup.d.ts.map +0 -1
  1052. package/dist/editor/test-setup.js +0 -42
  1053. package/dist/editor/test-setup.js.map +0 -1
  1054. package/dist/editor/theme.d.ts.map +0 -1
  1055. package/dist/editor/theme.js.map +0 -1
  1056. package/dist/editor/utils/env.d.ts.map +0 -1
  1057. package/dist/editor/utils/env.js.map +0 -1
  1058. package/dist/entry-link-resolver.d.ts.map +0 -1
  1059. package/dist/entry-link-resolver.js.map +0 -1
  1060. package/dist/entry-schema-registry.d.ts.map +0 -1
  1061. package/dist/entry-schema-registry.js.map +0 -1
  1062. package/dist/entry-schema.d.ts.map +0 -1
  1063. package/dist/entry-schema.js.map +0 -1
  1064. package/dist/git-manager.d.ts.map +0 -1
  1065. package/dist/git-manager.js.map +0 -1
  1066. package/dist/github-service.d.ts.map +0 -1
  1067. package/dist/github-service.js.map +0 -1
  1068. package/dist/http/handler.d.ts.map +0 -1
  1069. package/dist/http/handler.js.map +0 -1
  1070. package/dist/http/index.d.ts.map +0 -1
  1071. package/dist/http/index.js.map +0 -1
  1072. package/dist/http/router.d.ts.map +0 -1
  1073. package/dist/http/router.js.map +0 -1
  1074. package/dist/http/types.d.ts.map +0 -1
  1075. package/dist/http/types.js.map +0 -1
  1076. package/dist/id.d.ts.map +0 -1
  1077. package/dist/id.js.map +0 -1
  1078. package/dist/index.d.ts.map +0 -1
  1079. package/dist/index.js.map +0 -1
  1080. package/dist/operating-mode/client-safe-strategy.d.ts.map +0 -1
  1081. package/dist/operating-mode/client-safe-strategy.js.map +0 -1
  1082. package/dist/operating-mode/client-unsafe-strategy.d.ts.map +0 -1
  1083. package/dist/operating-mode/client-unsafe-strategy.js.map +0 -1
  1084. package/dist/operating-mode/client.d.ts.map +0 -1
  1085. package/dist/operating-mode/client.js.map +0 -1
  1086. package/dist/operating-mode/deployment-name-fixtures.d.ts.map +0 -1
  1087. package/dist/operating-mode/deployment-name-fixtures.js.map +0 -1
  1088. package/dist/operating-mode/deployment-name.d.ts.map +0 -1
  1089. package/dist/operating-mode/deployment-name.js.map +0 -1
  1090. package/dist/operating-mode/index.d.ts.map +0 -1
  1091. package/dist/operating-mode/index.js.map +0 -1
  1092. package/dist/operating-mode/mode-env.d.ts.map +0 -1
  1093. package/dist/operating-mode/mode-env.js.map +0 -1
  1094. package/dist/operating-mode/types.d.ts.map +0 -1
  1095. package/dist/operating-mode/types.js.map +0 -1
  1096. package/dist/paths/branch-name.d.ts.map +0 -1
  1097. package/dist/paths/branch-name.js.map +0 -1
  1098. package/dist/paths/branch.d.ts.map +0 -1
  1099. package/dist/paths/branch.js.map +0 -1
  1100. package/dist/paths/index.d.ts.map +0 -1
  1101. package/dist/paths/index.js.map +0 -1
  1102. package/dist/paths/normalize-server.d.ts.map +0 -1
  1103. package/dist/paths/normalize-server.js.map +0 -1
  1104. package/dist/paths/normalize.d.ts.map +0 -1
  1105. package/dist/paths/normalize.js.map +0 -1
  1106. package/dist/paths/resolve.d.ts.map +0 -1
  1107. package/dist/paths/resolve.js.map +0 -1
  1108. package/dist/paths/test-utils.d.ts +0 -22
  1109. package/dist/paths/test-utils.d.ts.map +0 -1
  1110. package/dist/paths/test-utils.js +0 -21
  1111. package/dist/paths/test-utils.js.map +0 -1
  1112. package/dist/paths/types.d.ts.map +0 -1
  1113. package/dist/paths/types.js.map +0 -1
  1114. package/dist/paths/validation.d.ts.map +0 -1
  1115. package/dist/paths/validation.js.map +0 -1
  1116. package/dist/reference-resolver.d.ts.map +0 -1
  1117. package/dist/reference-resolver.js.map +0 -1
  1118. package/dist/resolve-canopy-user.d.ts.map +0 -1
  1119. package/dist/resolve-canopy-user.js.map +0 -1
  1120. package/dist/resource-generation.d.ts.map +0 -1
  1121. package/dist/resource-generation.js.map +0 -1
  1122. package/dist/schema/index.d.ts.map +0 -1
  1123. package/dist/schema/index.js.map +0 -1
  1124. package/dist/schema/meta-loader.d.ts.map +0 -1
  1125. package/dist/schema/meta-loader.js.map +0 -1
  1126. package/dist/schema/resolver.d.ts.map +0 -1
  1127. package/dist/schema/resolver.js.map +0 -1
  1128. package/dist/schema/schema-store-types.d.ts.map +0 -1
  1129. package/dist/schema/schema-store-types.js.map +0 -1
  1130. package/dist/schema/schema-store.d.ts.map +0 -1
  1131. package/dist/schema/schema-store.js.map +0 -1
  1132. package/dist/schema/types.d.ts.map +0 -1
  1133. package/dist/schema/types.js.map +0 -1
  1134. package/dist/server.d.ts.map +0 -1
  1135. package/dist/server.js.map +0 -1
  1136. package/dist/services.d.ts.map +0 -1
  1137. package/dist/services.js.map +0 -1
  1138. package/dist/settings-workspace.d.ts.map +0 -1
  1139. package/dist/settings-workspace.js.map +0 -1
  1140. package/dist/static/index.d.ts.map +0 -1
  1141. package/dist/static/index.js.map +0 -1
  1142. package/dist/static/seo.d.ts.map +0 -1
  1143. package/dist/static/seo.js.map +0 -1
  1144. package/dist/sync-core.d.ts.map +0 -1
  1145. package/dist/sync-core.js.map +0 -1
  1146. package/dist/task-queue/index.d.ts.map +0 -1
  1147. package/dist/task-queue/index.js.map +0 -1
  1148. package/dist/task-queue/task-queue.d.ts.map +0 -1
  1149. package/dist/task-queue/task-queue.js.map +0 -1
  1150. package/dist/task-queue/types.d.ts.map +0 -1
  1151. package/dist/task-queue/types.js.map +0 -1
  1152. package/dist/types.d.ts.map +0 -1
  1153. package/dist/types.js.map +0 -1
  1154. package/dist/url-path-resolver.d.ts.map +0 -1
  1155. package/dist/url-path-resolver.js.map +0 -1
  1156. package/dist/user.d.ts.map +0 -1
  1157. package/dist/user.js.map +0 -1
  1158. package/dist/utils/async-mutex.d.ts.map +0 -1
  1159. package/dist/utils/async-mutex.js.map +0 -1
  1160. package/dist/utils/atomic-write.d.ts.map +0 -1
  1161. package/dist/utils/atomic-write.js.map +0 -1
  1162. package/dist/utils/body-field.d.ts.map +0 -1
  1163. package/dist/utils/body-field.js.map +0 -1
  1164. package/dist/utils/content-write-lock.d.ts.map +0 -1
  1165. package/dist/utils/content-write-lock.js.map +0 -1
  1166. package/dist/utils/debug.d.ts.map +0 -1
  1167. package/dist/utils/debug.js.map +0 -1
  1168. package/dist/utils/entry-url.d.ts.map +0 -1
  1169. package/dist/utils/entry-url.js.map +0 -1
  1170. package/dist/utils/error.d.ts.map +0 -1
  1171. package/dist/utils/error.js.map +0 -1
  1172. package/dist/utils/flatten-group-fields.d.ts.map +0 -1
  1173. package/dist/utils/flatten-group-fields.js.map +0 -1
  1174. package/dist/utils/format.d.ts.map +0 -1
  1175. package/dist/utils/format.js.map +0 -1
  1176. package/dist/utils/fs.d.ts.map +0 -1
  1177. package/dist/utils/fs.js.map +0 -1
  1178. package/dist/utils/git.d.ts.map +0 -1
  1179. package/dist/utils/git.js.map +0 -1
  1180. package/dist/utils/logger.d.ts.map +0 -1
  1181. package/dist/utils/logger.js.map +0 -1
  1182. package/dist/utils/occ-json-write.d.ts.map +0 -1
  1183. package/dist/utils/occ-json-write.js.map +0 -1
  1184. package/dist/utils/provisioning-lock.d.ts.map +0 -1
  1185. package/dist/utils/provisioning-lock.js.map +0 -1
  1186. package/dist/utils/sanitize-href.d.ts.map +0 -1
  1187. package/dist/utils/sanitize-href.js.map +0 -1
  1188. package/dist/utils/title-field.d.ts.map +0 -1
  1189. package/dist/utils/title-field.js.map +0 -1
  1190. package/dist/validation/deletion-checker.d.ts.map +0 -1
  1191. package/dist/validation/deletion-checker.js.map +0 -1
  1192. package/dist/validation/entry-link-validator.d.ts.map +0 -1
  1193. package/dist/validation/entry-link-validator.js.map +0 -1
  1194. package/dist/validation/entry-type-reference-validator.d.ts.map +0 -1
  1195. package/dist/validation/entry-type-reference-validator.js.map +0 -1
  1196. package/dist/validation/entry-validator.d.ts.map +0 -1
  1197. package/dist/validation/entry-validator.js.map +0 -1
  1198. package/dist/validation/field-traversal.d.ts.map +0 -1
  1199. package/dist/validation/field-traversal.js.map +0 -1
  1200. package/dist/validation/reference-validator.d.ts.map +0 -1
  1201. package/dist/validation/reference-validator.js.map +0 -1
  1202. package/dist/worker/cms-worker.d.ts.map +0 -1
  1203. package/dist/worker/cms-worker.js.map +0 -1
  1204. package/dist/worker/log.d.ts.map +0 -1
  1205. package/dist/worker/log.js.map +0 -1
  1206. package/dist/worker/task-queue-config.d.ts.map +0 -1
  1207. package/dist/worker/task-queue-config.js.map +0 -1
  1208. package/dist/worker/task-queue.d.ts.map +0 -1
  1209. package/dist/worker/task-queue.js.map +0 -1
  1210. package/dist/worker/worker-status.d.ts.map +0 -1
  1211. package/dist/worker/worker-status.js.map +0 -1
package/dist/cli/cli.js CHANGED
@@ -592,7 +592,7 @@ async function recoverOrphanedTasks(taskDir, maxAgeMs = 5 * 6e4, logger = nullLo
592
592
  async function cleanupOldTasks(taskDir, maxAgeMs = 30 * 24 * 60 * 6e4, logger = nullLogger) {
593
593
  const now = Date.now();
594
594
  let cleaned = 0;
595
- for (const subdir of ["completed", "failed"]) {
595
+ for (const subdir of ["completed", "failed", "corrupt"]) {
596
596
  const dir = path6.join(taskDir, subdir);
597
597
  let files;
598
598
  try {
@@ -810,6 +810,13 @@ function configImportPath(appDir, subdirs) {
810
810
  const totalDepth = appDepth + subdirs;
811
811
  return "../".repeat(totalDepth) + "canopycms.config";
812
812
  }
813
+ async function firstExistingPath(projectDir, candidates) {
814
+ for (const candidate of candidates) {
815
+ if (await filePathExists2(path7.join(projectDir, candidate)))
816
+ return candidate;
817
+ }
818
+ return null;
819
+ }
813
820
  async function init(options) {
814
821
  const { projectDir, mode, appDir, ai, force, nonInteractive } = options;
815
822
  const writeOpts = { force, nonInteractive };
@@ -864,15 +871,35 @@ async function init(options) {
864
871
  await writeFile(path7.join(projectDir, appDir, "ai/config.ts"), await aiConfig2(), writeOpts);
865
872
  await writeFile(path7.join(projectDir, appDir, `ai/[...path]/${serverRouteExt}`), await aiRoute2({ configImport: configImportPath(appDir, 2) }), writeOpts);
866
873
  }
867
- await writeFile(path7.join(projectDir, "next.config.ts"), await nextConfig2({ staticBuild }), writeOpts);
868
- await writeFile(path7.join(projectDir, "middleware.ts"), await middleware2({ authProvider }), writeOpts);
874
+ const existingJsConfig = await firstExistingPath(projectDir, [
875
+ "next.config.js",
876
+ "next.config.mjs"
877
+ ]);
878
+ if (existingJsConfig) {
879
+ p.note([
880
+ `Found ${existingJsConfig}, which Next loads in preference to next.config.ts.`,
881
+ "Left it untouched rather than writing a second config Next would ignore.",
882
+ "",
883
+ "Wrap your existing config by hand:",
884
+ "",
885
+ " import { withCanopy } from 'canopycms-next/config'",
886
+ " export default withCanopy(yourConfig)"
887
+ ].join("\n"), "Manual step");
888
+ } else {
889
+ await writeFile(path7.join(projectDir, "next.config.ts"), await nextConfig2({ staticBuild }), writeOpts);
890
+ }
891
+ await writeFile(path7.join(projectDir, path7.dirname(appDir), "middleware.ts"), await middleware2({ authProvider }), writeOpts);
869
892
  const gitignorePath = path7.join(projectDir, ".gitignore");
893
+ const CANOPY_GITIGNORE_BLOCK = "# CanopyCMS\n.canopy-dev/\n";
870
894
  if (await filePathExists2(gitignorePath)) {
871
895
  const content = await fs6.readFile(gitignorePath, "utf-8");
872
896
  if (!content.includes(".canopy-dev")) {
873
- await fs6.appendFile(gitignorePath, "\n# CanopyCMS\n.canopy-dev/\n");
897
+ await fs6.appendFile(gitignorePath, `
898
+ ${CANOPY_GITIGNORE_BLOCK}`);
874
899
  p.log.success("updated: .gitignore");
875
900
  }
901
+ } else {
902
+ await writeFile(gitignorePath, CANOPY_GITIGNORE_BLOCK, writeOpts);
876
903
  }
877
904
  const packages = authProvider === "clerk" ? "canopycms canopycms-next canopycms-auth-clerk canopycms-auth-dev" : "canopycms canopycms-next canopycms-auth-dev";
878
905
  p.note([
@@ -1047,7 +1074,7 @@ CanopyCMS worker run-once (mode: ${mode}, auth: ${authMode})
1047
1074
  }
1048
1075
  console.log("\nDone");
1049
1076
  }
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;
1077
+ 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
1078
  var init_init = __esm({
1052
1079
  "dist/cli/init.js"() {
1053
1080
  "use strict";
@@ -1134,7 +1161,12 @@ var init_init = __esm({
1134
1161
  collections: z.array(z.string().min(1)).min(1).optional(),
1135
1162
  entryTypes: z.array(z.string().min(1)).min(1).optional(),
1136
1163
  displayField: z.string().min(1).optional(),
1137
- options: z.array(referenceOptionSchema).optional()
1164
+ options: z.array(referenceOptionSchema).optional(),
1165
+ // Must be listed here, not just on ReferenceFieldConfig: zod strips unknown keys by
1166
+ // default, so a consumer that adopts this schema's parse output would silently delete a
1167
+ // runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
1168
+ // absent on purpose — it is type-inference-only and stripping it changes no behavior.)
1169
+ includeBody: z.boolean().optional()
1138
1170
  });
1139
1171
  fieldHolder = [z.never()];
1140
1172
  blockSchema = z.object({
@@ -1255,6 +1287,9 @@ var init_init = __esm({
1255
1287
  deployedAsSchema = z4.enum(["static", "server"]).default("server");
1256
1288
  contentRootSchema = relativePathSchema.default("content");
1257
1289
  sourceRootSchema = z4.string().min(1).optional();
1290
+ basePathSchema = z4.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
1291
+ message: "basePath must not be empty or only whitespace"
1292
+ }).optional();
1258
1293
  deploymentNameSchema = z4.string().default("prod");
1259
1294
  devContentSyncSchema = z4.enum(["off", "warn"]).default("warn");
1260
1295
  devConfigSchema = z4.object({
@@ -1317,6 +1352,7 @@ var init_init = __esm({
1317
1352
  deploymentName: deploymentNameSchema.optional(),
1318
1353
  contentRoot: contentRootSchema.default("content"),
1319
1354
  sourceRoot: sourceRootSchema.optional(),
1355
+ basePath: basePathSchema,
1320
1356
  editor: editorConfigSchema.optional(),
1321
1357
  authPlugin: z4.custom().optional(),
1322
1358
  entryLinkUrl: z4.custom().optional(),
@@ -1480,6 +1516,7 @@ var init_init = __esm({
1480
1516
  };
1481
1517
  MAX_WORKSPACE_DEPTH = 8;
1482
1518
  CDK_DEPENDENCIES = [
1519
+ "canopycms",
1483
1520
  "canopycms-cdk",
1484
1521
  "aws-cdk-lib",
1485
1522
  "constructs",
@@ -1844,63 +1881,73 @@ import { isAbsolute as isAbsolute2 } from "pathe";
1844
1881
  import { z as z42 } from "zod";
1845
1882
  import { z as z52 } from "zod";
1846
1883
  import { z as z6 } from "zod";
1847
- import path82 from "node:path";
1848
- import path19 from "node:path";
1884
+ import path9 from "node:path";
1885
+ import path20 from "node:path";
1849
1886
  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";
1887
+ import fs17 from "node:fs/promises";
1888
+ import path19 from "node:path";
1889
+ import fs10 from "node:fs/promises";
1890
+ import path11 from "node:path";
1891
+ import matter2 from "gray-matter";
1892
+ import { parse as yamlParse } from "yaml";
1893
+ import fs7 from "node:fs/promises";
1853
1894
  import path10 from "node:path";
1854
1895
  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";
1896
+ import { isCollection, isMap, isNode, isScalar, isSeq, parseDocument, stringify as yamlStringify } from "yaml";
1858
1897
  import path32 from "node:path";
1859
1898
  import fs23 from "node:fs/promises";
1860
1899
  import path22 from "node:path";
1861
1900
  import lockfile from "proper-lockfile";
1901
+ import fs42 from "node:fs/promises";
1902
+ import path52 from "node:path";
1862
1903
  import fs32 from "node:fs/promises";
1863
1904
  import path42 from "node:path";
1864
1905
  import { generate } from "short-uuid";
1865
- import path52 from "node:path";
1866
- import fs42 from "node:fs/promises";
1867
1906
  import path62 from "node:path";
1868
- import { randomUUID } from "node:crypto";
1869
- import fs62 from "node:fs/promises";
1907
+ import fs52 from "node:fs/promises";
1870
1908
  import path72 from "node:path";
1871
- import { promises as fs52 } from "fs";
1909
+ import { randomUUID } from "node:crypto";
1910
+ import fs72 from "node:fs/promises";
1911
+ import path82 from "node:path";
1912
+ import { promises as fs62 } from "fs";
1872
1913
  import { join as join3 } from "pathe";
1873
1914
  import { z as z7 } from "zod";
1874
1915
  import chokidar from "chokidar";
1875
- import fs72 from "node:fs/promises";
1876
- import { sep, resolve, relative } from "node:path";
1877
1916
  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";
1917
+ import { sep, resolve, relative } from "node:path";
1918
+ import fs9 from "node:fs/promises";
1919
+ import path102 from "node:path";
1886
1920
  import path12 from "node:path";
1921
+ import fs11 from "node:fs/promises";
1922
+ import { minimatch } from "minimatch";
1923
+ import path17 from "node:path";
1924
+ import fs14 from "node:fs/promises";
1925
+ import path15 from "node:path";
1887
1926
  import fs12 from "node:fs/promises";
1888
1927
  import path13 from "node:path";
1928
+ import fs13 from "node:fs/promises";
1929
+ import path14 from "node:path";
1889
1930
  import { randomUUID as randomUUID2 } from "node:crypto";
1890
1931
  import lockfile2 from "proper-lockfile";
1891
- import fs14 from "node:fs/promises";
1892
- import path15 from "node:path";
1932
+ import fs15 from "node:fs/promises";
1933
+ import path16 from "node:path";
1893
1934
  import { simpleGit as simpleGit2 } from "simple-git";
1894
1935
  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";
1936
+ import fs16 from "node:fs/promises";
1937
+ import path18 from "node:path";
1938
+ import matter3 from "gray-matter";
1898
1939
  import { parse as yamlParse2 } from "yaml";
1899
- function normalizeFilesystemPath(path202) {
1900
- return path202.split(/[\\/]+/).filter(Boolean).join("/");
1940
+ function canopyLogWarn2(...args) {
1941
+ active2.warn(...args);
1942
+ }
1943
+ function canopyLogError(...args) {
1944
+ active2.error(...args);
1901
1945
  }
1902
- function hasTraversalSequence(path202) {
1903
- const normalized = normalizeFilesystemPath(path202);
1946
+ function normalizeFilesystemPath(path212) {
1947
+ return path212.split(/[\\/]+/).filter(Boolean).join("/");
1948
+ }
1949
+ function hasTraversalSequence(path212) {
1950
+ const normalized = normalizeFilesystemPath(path212);
1904
1951
  return normalized.includes("..");
1905
1952
  }
1906
1953
  function createLogicalPath(...segments) {
@@ -1910,20 +1957,14 @@ function createLogicalPath(...segments) {
1910
1957
  }
1911
1958
  return normalized;
1912
1959
  }
1913
- function trimSlashes(path202) {
1960
+ function trimSlashes(path212) {
1914
1961
  let start = 0;
1915
- let end = path202.length;
1916
- while (start < end && path202[start] === "/")
1962
+ let end = path212.length;
1963
+ while (start < end && path212[start] === "/")
1917
1964
  start++;
1918
- while (end > start && path202[end - 1] === "/")
1965
+ while (end > start && path212[end - 1] === "/")
1919
1966
  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);
1967
+ return path212.slice(start, end);
1927
1968
  }
1928
1969
  function readModeEnv() {
1929
1970
  if (typeof process === "undefined" || typeof process.env === "undefined")
@@ -2007,8 +2048,26 @@ function operatingStrategy2(mode) {
2007
2048
  function clearStrategyCache() {
2008
2049
  strategyCache2.clear();
2009
2050
  }
2051
+ function getErrorMessage3(err) {
2052
+ if (err instanceof Error) {
2053
+ return err.message;
2054
+ }
2055
+ if (typeof err === "string") {
2056
+ return err;
2057
+ }
2058
+ return String(err);
2059
+ }
2060
+ function isNodeError3(err) {
2061
+ return err instanceof Error && "code" in err;
2062
+ }
2063
+ function isNotFoundError3(err) {
2064
+ return isNodeError3(err) && err.code === "ENOENT";
2065
+ }
2066
+ function isFileExistsError2(err) {
2067
+ return isNodeError3(err) && err.code === "EEXIST";
2068
+ }
2010
2069
  async function atomicWriteFile2(filePath, content) {
2011
- const dir = path9.dirname(filePath);
2070
+ const dir = path10.dirname(filePath);
2012
2071
  await fs7.mkdir(dir, { recursive: true });
2013
2072
  const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
2014
2073
  await fs7.writeFile(tempPath, content, typeof content === "string" ? "utf-8" : void 0);
@@ -2020,6 +2079,196 @@ async function atomicWriteFile2(filePath, content) {
2020
2079
  throw err;
2021
2080
  }
2022
2081
  }
2082
+ function isBlockStructuralKey(key) {
2083
+ return BLOCK_STRUCTURAL_KEYS.has(key);
2084
+ }
2085
+ function isPlainRecord(value) {
2086
+ if (typeof value !== "object" || value === null || Array.isArray(value))
2087
+ return false;
2088
+ const proto = Object.getPrototypeOf(value);
2089
+ return proto === Object.prototype || proto === null;
2090
+ }
2091
+ function recordKeyOf(keyNode) {
2092
+ if (!isScalar(keyNode))
2093
+ return void 0;
2094
+ const { value } = keyNode;
2095
+ if (value === null || value === void 0)
2096
+ return void 0;
2097
+ if (typeof value === "object")
2098
+ return void 0;
2099
+ return String(value);
2100
+ }
2101
+ function carryComments(from, to) {
2102
+ if (!isNode(from) || !isNode(to))
2103
+ return;
2104
+ const source = from;
2105
+ const target = to;
2106
+ if (source.commentBefore !== void 0)
2107
+ target.commentBefore = source.commentBefore;
2108
+ if (source.comment !== void 0)
2109
+ target.comment = source.comment;
2110
+ if (source.spaceBefore !== void 0)
2111
+ target.spaceBefore = source.spaceBefore;
2112
+ }
2113
+ function identityKey(value) {
2114
+ try {
2115
+ return JSON.stringify(value) ?? "undefined";
2116
+ } catch {
2117
+ return void 0;
2118
+ }
2119
+ }
2120
+ function nodePlainValue(node) {
2121
+ if (!isNode(node))
2122
+ return node;
2123
+ try {
2124
+ return node.toJSON();
2125
+ } catch {
2126
+ return void 0;
2127
+ }
2128
+ }
2129
+ function nodeIdentityKey(node) {
2130
+ const plain = nodePlainValue(node);
2131
+ return plain === void 0 && isNode(node) ? void 0 : identityKey(plain);
2132
+ }
2133
+ function sharesFieldEvidence(existing, value, depth) {
2134
+ for (const key of Object.keys(value)) {
2135
+ if (isBlockStructuralKey(key))
2136
+ continue;
2137
+ if (!Object.prototype.hasOwnProperty.call(existing, key))
2138
+ continue;
2139
+ const before = existing[key];
2140
+ const after = value[key];
2141
+ if (depth < EVIDENCE_MAX_DEPTH && isPlainRecord(before) && isPlainRecord(after) && sharesFieldEvidence(before, after, depth + 1)) {
2142
+ return true;
2143
+ }
2144
+ const beforeKey = identityKey(before);
2145
+ if (beforeKey !== void 0 && beforeKey === identityKey(after))
2146
+ return true;
2147
+ }
2148
+ return false;
2149
+ }
2150
+ function looksLikeSameItem(node, value) {
2151
+ const existing = nodePlainValue(node);
2152
+ if (!isPlainRecord(existing) || !isPlainRecord(value))
2153
+ return true;
2154
+ return sharesFieldEvidence(existing, value, 0);
2155
+ }
2156
+ function reconcileNode(doc, existing, value) {
2157
+ if (isMap(existing) && isPlainRecord(value)) {
2158
+ reconcileMap(doc, existing, value);
2159
+ return existing;
2160
+ }
2161
+ if (isSeq(existing) && Array.isArray(value)) {
2162
+ reconcileSeq(doc, existing, value);
2163
+ return existing;
2164
+ }
2165
+ if (isScalar(existing) && Object.is(existing.value, value))
2166
+ return existing;
2167
+ const fresh = doc.createNode(value);
2168
+ if (!isCollection(existing))
2169
+ carryComments(existing, fresh);
2170
+ return fresh;
2171
+ }
2172
+ function reconcileMap(doc, map, value) {
2173
+ const wanted = new Set(Object.keys(value).filter((key) => value[key] !== void 0));
2174
+ const seen = /* @__PURE__ */ new Set();
2175
+ const retained = [];
2176
+ for (const pair of map.items) {
2177
+ const key = recordKeyOf(pair.key);
2178
+ if (key === void 0 || !wanted.has(key) || seen.has(key))
2179
+ continue;
2180
+ seen.add(key);
2181
+ pair.value = reconcileNode(doc, pair.value, value[key]);
2182
+ retained.push(pair);
2183
+ }
2184
+ map.items = retained;
2185
+ for (const key of Object.keys(value)) {
2186
+ if (seen.has(key) || !wanted.has(key))
2187
+ continue;
2188
+ map.set(doc.createNode(key), doc.createNode(value[key]));
2189
+ }
2190
+ }
2191
+ function reconcileSeq(doc, seq, value) {
2192
+ const oldItems = seq.items;
2193
+ const byIdentity = /* @__PURE__ */ new Map();
2194
+ oldItems.forEach((node, index) => {
2195
+ const key = nodeIdentityKey(node);
2196
+ if (key === void 0)
2197
+ return;
2198
+ const bucket = byIdentity.get(key);
2199
+ if (bucket)
2200
+ bucket.push(index);
2201
+ else
2202
+ byIdentity.set(key, [index]);
2203
+ });
2204
+ const consumed = /* @__PURE__ */ new Set();
2205
+ const matches = value.map((item) => {
2206
+ const key = identityKey(item);
2207
+ if (key === void 0)
2208
+ return void 0;
2209
+ const bucket = byIdentity.get(key);
2210
+ while (bucket && bucket.length > 0) {
2211
+ const index = bucket.shift();
2212
+ if (index !== void 0 && !consumed.has(index)) {
2213
+ consumed.add(index);
2214
+ return index;
2215
+ }
2216
+ }
2217
+ return void 0;
2218
+ });
2219
+ seq.items = value.map((item, index) => {
2220
+ const matched = matches[index];
2221
+ if (matched !== void 0)
2222
+ return oldItems[matched];
2223
+ const candidate = index < oldItems.length && !consumed.has(index) ? oldItems[index] : void 0;
2224
+ const sameItem = candidate !== void 0 && looksLikeSameItem(candidate, item) ? candidate : void 0;
2225
+ return reconcileNode(doc, sameItem, item);
2226
+ });
2227
+ }
2228
+ function applyDataToDocument(doc, data) {
2229
+ doc.contents = reconcileNode(doc, doc.contents, data);
2230
+ }
2231
+ function serializeYaml(data, existingRaw) {
2232
+ if (existingRaw === void 0)
2233
+ return yamlStringify(data);
2234
+ const doc = parseDocument(existingRaw);
2235
+ if (doc.errors.length > 0)
2236
+ return yamlStringify(data);
2237
+ applyDataToDocument(doc, data);
2238
+ return doc.toString();
2239
+ }
2240
+ function extractRawFrontmatter(raw) {
2241
+ let parsed;
2242
+ try {
2243
+ parsed = matter(raw, {});
2244
+ } catch {
2245
+ return void 0;
2246
+ }
2247
+ const frontmatter = parsed.matter;
2248
+ if (typeof frontmatter !== "string" || frontmatter.trim() === "")
2249
+ return void 0;
2250
+ return frontmatter;
2251
+ }
2252
+ function serializeFrontmatter(body, data, existingRaw) {
2253
+ if (existingRaw === void 0)
2254
+ return matter.stringify(body, data);
2255
+ const existingFrontmatter = extractRawFrontmatter(existingRaw);
2256
+ if (existingFrontmatter === void 0)
2257
+ return matter.stringify(body, data);
2258
+ const doc = parseDocument(existingFrontmatter);
2259
+ if (doc.errors.length > 0)
2260
+ return matter.stringify(body, data);
2261
+ applyDataToDocument(doc, data);
2262
+ const reconciled = doc.toString();
2263
+ return matter.stringify(body, data, {
2264
+ engines: {
2265
+ yaml: {
2266
+ parse: () => ({}),
2267
+ stringify: () => reconciled
2268
+ }
2269
+ }
2270
+ });
2271
+ }
2023
2272
  async function withLock(key, fn) {
2024
2273
  const prev = locks.get(key) ?? Promise.resolve();
2025
2274
  let resolve2;
@@ -2036,51 +2285,71 @@ async function withLock(key, fn) {
2036
2285
  locks.delete(key);
2037
2286
  }
2038
2287
  }
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";
2288
+ function provisioningLockOptions(lockPath, retries, onCompromised) {
2289
+ return {
2290
+ lockfilePath: lockPath,
2291
+ realpath: false,
2292
+ retries,
2293
+ stale: 3e4,
2294
+ // proper-lockfile invokes this from inside its refresh timer, so ANY throw
2295
+ // escaping here is an uncaught exception that kills the process -- the very
2296
+ // failure the handler exists to prevent. Call sites are told not to throw
2297
+ // (see OnLockCompromised); this makes it structural rather than a
2298
+ // convention, and also covers the LOGGER throwing: under `CI=true`,
2299
+ // vitest's `onConsoleLog` turns any console write into a throw.
2300
+ onCompromised: (err) => {
2301
+ try {
2302
+ if (onCompromised) {
2303
+ onCompromised(err);
2304
+ return;
2305
+ }
2306
+ canopyLogWarn2(`[canopy] Provisioning lock compromised mid-hold for ${lockPath}:`, getErrorMessage3(err));
2307
+ } catch {
2308
+ try {
2309
+ process.stderr.write(`[canopy] lock compromised for ${lockPath}; its handler or logger threw
2310
+ `);
2311
+ } catch {
2312
+ }
2313
+ }
2314
+ }
2315
+ };
2053
2316
  }
2054
- function isFileExistsError2(err) {
2055
- return isNodeError3(err) && err.code === "EEXIST";
2317
+ function releaseIgnoringAlreadyReleased(release, lockPath) {
2318
+ return async () => {
2319
+ try {
2320
+ await release();
2321
+ } catch (err) {
2322
+ if (isNodeError3(err) && err.code === "ERELEASED") {
2323
+ canopyLogWarn2(`[canopy] Lock at ${lockPath} was already released (compromised mid-hold, or released twice); nothing to release`);
2324
+ return;
2325
+ }
2326
+ throw err;
2327
+ }
2328
+ };
2056
2329
  }
2057
- async function acquireProvisioningLock(lockTargetDir2, lockName) {
2330
+ async function acquireProvisioningLock(lockTargetDir2, lockName, onCompromised) {
2058
2331
  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
- });
2332
+ const lockPath = path22.join(lockTargetDir2, lockName);
2333
+ const release = await lockfile.lock(lockPath, provisioningLockOptions(lockPath, { retries: 600, factor: 1, minTimeout: 300, maxTimeout: 800, randomize: true }, onCompromised));
2334
+ return releaseIgnoringAlreadyReleased(release, lockPath);
2064
2335
  }
2065
- async function tryAcquireProvisioningLock(lockTargetDir2, lockName) {
2336
+ async function tryAcquireProvisioningLock(lockTargetDir2, lockName, onCompromised) {
2066
2337
  await fs23.mkdir(lockTargetDir2, { recursive: true });
2067
- return lockfile.lock(lockTargetDir2, {
2068
- lockfilePath: path22.join(lockTargetDir2, lockName),
2069
- retries: 0,
2070
- stale: 3e4
2071
- });
2338
+ const lockPath = path22.join(lockTargetDir2, lockName);
2339
+ const release = await lockfile.lock(lockPath, provisioningLockOptions(lockPath, 0, onCompromised));
2340
+ return releaseIgnoringAlreadyReleased(release, lockPath);
2072
2341
  }
2073
2342
  function lockTargetDir(branchRoot) {
2074
2343
  return path32.join(path32.resolve(branchRoot), META_DIR);
2075
2344
  }
2076
- function tryAcquireContentWriteLock(branchRoot) {
2077
- return tryAcquireProvisioningLock(lockTargetDir(branchRoot), CONTENT_WRITE_LOCK_NAME);
2345
+ function tryAcquireContentWriteLock(branchRoot, onCompromised) {
2346
+ return tryAcquireProvisioningLock(lockTargetDir(branchRoot), CONTENT_WRITE_LOCK_NAME, onCompromised);
2078
2347
  }
2079
- async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS) {
2348
+ async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, onCompromised) {
2080
2349
  const startedAt = Date.now();
2081
2350
  for (let attempt = 0; ; attempt++) {
2082
2351
  try {
2083
- return await tryAcquireContentWriteLock(branchRoot);
2352
+ return await tryAcquireContentWriteLock(branchRoot, onCompromised);
2084
2353
  } catch (err) {
2085
2354
  if (!isNodeError3(err) || err.code !== "ELOCKED")
2086
2355
  throw err;
@@ -2093,12 +2362,21 @@ async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRIT
2093
2362
  }
2094
2363
  }
2095
2364
  async function withContentWriteLock(branchRoot, fn, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS) {
2096
- const release = await acquireContentWriteLock(branchRoot, waitMs);
2365
+ let compromised = false;
2366
+ const release = await acquireContentWriteLock(branchRoot, waitMs, (err) => {
2367
+ compromised = true;
2368
+ canopyLogWarn2(`[canopy] Content-write lock compromised mid-hold for ${branchRoot}:`, getErrorMessage3(err));
2369
+ });
2370
+ let result;
2097
2371
  try {
2098
- return await fn();
2372
+ result = await fn();
2099
2373
  } finally {
2100
2374
  await release();
2101
2375
  }
2376
+ if (compromised) {
2377
+ 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.");
2378
+ }
2379
+ return result;
2102
2380
  }
2103
2381
  function flattenGroupFields(fields) {
2104
2382
  const result = [];
@@ -2118,9 +2396,68 @@ function findBodyFieldName(fields) {
2118
2396
  }
2119
2397
  return "body";
2120
2398
  }
2399
+ function buildResolvedReference(data, meta, body) {
2400
+ const resolved = { ...data };
2401
+ if (body && body.value)
2402
+ resolved[body.fieldName] = body.value;
2403
+ resolved.id = meta.id;
2404
+ resolved.slug = meta.slug;
2405
+ resolved.collection = meta.collection;
2406
+ resolved.urlPath = meta.urlPath;
2407
+ return resolved;
2408
+ }
2409
+ function isIndexSlug(slug) {
2410
+ return slug?.toLowerCase() === "index";
2411
+ }
2412
+ function computeEntryUrl(collection, slug, contentRoot) {
2413
+ const root = trimSlashes(contentRoot);
2414
+ let stripped = collection;
2415
+ if (root && collection.startsWith(`${root}/`)) {
2416
+ stripped = collection.slice(root.length + 1);
2417
+ } else if (collection === root) {
2418
+ stripped = "";
2419
+ }
2420
+ const segments = stripped.split("/").filter(Boolean);
2421
+ if (slug && !isIndexSlug(slug)) {
2422
+ segments.push(slug);
2423
+ }
2424
+ const path212 = segments.length > 0 ? `/${segments.join("/")}` : "/";
2425
+ return path212.toLowerCase();
2426
+ }
2121
2427
  function isValidContentId(id) {
2122
2428
  return CONTENT_ID_PATTERN.test(id);
2123
2429
  }
2430
+ function parseSlug(slug) {
2431
+ if (!slug) {
2432
+ return {
2433
+ ok: false,
2434
+ error: "Slug is required"
2435
+ };
2436
+ }
2437
+ if (slug.length > 64) {
2438
+ return { ok: false, error: "Slug too long (max 64 characters)" };
2439
+ }
2440
+ if (slug.includes("/") || slug.includes("\\")) {
2441
+ return {
2442
+ ok: false,
2443
+ error: "Slug cannot contain path separators"
2444
+ };
2445
+ }
2446
+ if (slug === "." || slug === "..") {
2447
+ return {
2448
+ ok: false,
2449
+ error: "Slug cannot be a traversal sequence"
2450
+ };
2451
+ }
2452
+ const normalized = slug.toLowerCase();
2453
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(normalized)) {
2454
+ return {
2455
+ ok: false,
2456
+ error: "Slug must start with a letter or number and contain only lowercase letters, numbers, and hyphens"
2457
+ };
2458
+ }
2459
+ return { ok: true, slug: normalized };
2460
+ }
2124
2461
  function generateId() {
2125
2462
  const full = generate();
2126
2463
  return full.substring(0, 12);
@@ -2148,13 +2485,13 @@ function extractIdFromFilename(filename) {
2148
2485
  return null;
2149
2486
  }
2150
2487
  async function resolveCollectionPath(root, logicalPath) {
2151
- const fs172 = await import("node:fs/promises");
2152
- const path202 = await import("node:path");
2488
+ const fs182 = await import("node:fs/promises");
2489
+ const path212 = await import("node:path");
2153
2490
  const segments = logicalPath.split("/").filter(Boolean);
2154
2491
  let currentPath = root;
2155
2492
  for (const segment of segments) {
2156
2493
  try {
2157
- const entries = await fs172.readdir(currentPath, { withFileTypes: true });
2494
+ const entries = await fs182.readdir(currentPath, { withFileTypes: true });
2158
2495
  const matchingDir = entries.find((entry) => {
2159
2496
  if (!entry.isDirectory())
2160
2497
  return false;
@@ -2162,7 +2499,7 @@ async function resolveCollectionPath(root, logicalPath) {
2162
2499
  return logicalName === segment.toLowerCase();
2163
2500
  });
2164
2501
  if (matchingDir) {
2165
- currentPath = path202.join(currentPath, matchingDir.name);
2502
+ currentPath = path212.join(currentPath, matchingDir.name);
2166
2503
  } else {
2167
2504
  return null;
2168
2505
  }
@@ -2211,8 +2548,70 @@ function extractSlugFromFilename(filename, entryTypeName) {
2211
2548
  }
2212
2549
  return filename.toLowerCase();
2213
2550
  }
2551
+ async function readDirSafe(dir) {
2552
+ try {
2553
+ return await fs42.readdir(dir, { withFileTypes: true });
2554
+ } catch (err) {
2555
+ if (isNotFoundError3(err))
2556
+ return [];
2557
+ throw err;
2558
+ }
2559
+ }
2560
+ async function findChildCollectionDir(dir, name) {
2561
+ const wanted = name.toLowerCase();
2562
+ for (const entry of await readDirSafe(dir)) {
2563
+ if (!entry.isDirectory())
2564
+ continue;
2565
+ if (extractSlugFromFilename(entry.name) === wanted)
2566
+ return path52.join(dir, entry.name);
2567
+ }
2568
+ return null;
2569
+ }
2570
+ function entrySlugOf(filename) {
2571
+ if (!CONTENT_EXTENSIONS.some((ext) => filename.endsWith(ext)))
2572
+ return null;
2573
+ return parseTypedFilename(filename)?.slug ?? null;
2574
+ }
2575
+ async function findIndexEntryIn(dir) {
2576
+ for (const entry of await readDirSafe(dir)) {
2577
+ if (entry.isDirectory())
2578
+ continue;
2579
+ if (isIndexSlug(entrySlugOf(entry.name) ?? void 0))
2580
+ return path52.join(dir, entry.name);
2581
+ }
2582
+ return null;
2583
+ }
2584
+ async function findEntryBySlugIn(dir, slug) {
2585
+ const wanted = slug.toLowerCase();
2586
+ for (const entry of await readDirSafe(dir)) {
2587
+ if (entry.isDirectory())
2588
+ continue;
2589
+ if (entrySlugOf(entry.name) === wanted)
2590
+ return path52.join(dir, entry.name);
2591
+ }
2592
+ return null;
2593
+ }
2594
+ async function findUrlPathClaimant(opts) {
2595
+ const { collectionDir, slug, contentRoot } = opts;
2596
+ if (isIndexSlug(slug)) {
2597
+ const resolvedRoot = path52.resolve(contentRoot);
2598
+ if (path52.resolve(collectionDir) === resolvedRoot)
2599
+ return null;
2600
+ const parentDir = path52.dirname(collectionDir);
2601
+ const ownName = extractSlugFromFilename(path52.basename(collectionDir));
2602
+ if (isIndexSlug(ownName))
2603
+ return null;
2604
+ const claimant = await findEntryBySlugIn(parentDir, ownName);
2605
+ return claimant ? { kind: "parent-entry", physicalPath: claimant, name: ownName } : null;
2606
+ }
2607
+ const childDir = await findChildCollectionDir(collectionDir, slug);
2608
+ if (!childDir)
2609
+ return null;
2610
+ const indexEntry = await findIndexEntryIn(childDir);
2611
+ return indexEntry ? { kind: "sibling-collection-index", physicalPath: indexEntry, name: slug } : null;
2612
+ }
2214
2613
  function registerContentIndexForInvalidation(root, target) {
2215
- const rootKey = path52.resolve(root);
2614
+ const rootKey = path62.resolve(root);
2216
2615
  let refs = registry.get(rootKey);
2217
2616
  if (!refs) {
2218
2617
  refs = /* @__PURE__ */ new Set();
@@ -2223,8 +2622,8 @@ function registerContentIndexForInvalidation(root, target) {
2223
2622
  finalization.register(target, { rootKey, ref });
2224
2623
  }
2225
2624
  function invalidateContentIndexesForRoot(root) {
2226
- const rootKey = path52.resolve(root);
2227
- const prefix = rootKey + path52.sep;
2625
+ const rootKey = path62.resolve(root);
2626
+ const prefix = rootKey + path62.sep;
2228
2627
  for (const [storeRoot, refs] of registry) {
2229
2628
  if (storeRoot !== rootKey && !storeRoot.startsWith(prefix))
2230
2629
  continue;
@@ -2244,7 +2643,7 @@ function createDebugLogger2(options) {
2244
2643
  return new DebugLogger2(options);
2245
2644
  }
2246
2645
  function resourceGenerationPath(root, resource) {
2247
- return path62.resolve(root, META_DIR2, `${resource}.generation`);
2646
+ return path72.resolve(root, META_DIR2, `${resource}.generation`);
2248
2647
  }
2249
2648
  async function bumpResourceGeneration(root, resource, options) {
2250
2649
  const token = randomUUID();
@@ -2262,7 +2661,7 @@ async function bumpResourceGeneration(root, resource, options) {
2262
2661
  }
2263
2662
  async function readResourceGeneration(root, resource) {
2264
2663
  try {
2265
- const token = await fs42.readFile(resourceGenerationPath(root, resource), "utf-8");
2664
+ const token = await fs52.readFile(resourceGenerationPath(root, resource), "utf-8");
2266
2665
  return { ok: true, token };
2267
2666
  } catch (err) {
2268
2667
  if (isNodeError3(err) && err.code === "ENOENT")
@@ -2282,7 +2681,7 @@ function stripEmbeddedIdFromName(name) {
2282
2681
  async function scanForCollectionMeta(baseDir, relativePath = "") {
2283
2682
  const collections = [];
2284
2683
  try {
2285
- const entries = await fs52.readdir(baseDir, { withFileTypes: true });
2684
+ const entries = await fs62.readdir(baseDir, { withFileTypes: true });
2286
2685
  for (const entry of entries) {
2287
2686
  if (!entry.isDirectory())
2288
2687
  continue;
@@ -2293,8 +2692,8 @@ async function scanForCollectionMeta(baseDir, relativePath = "") {
2293
2692
  const absolutePath = join3(baseDir, folderName);
2294
2693
  const metaPath = join3(absolutePath, ".collection.json");
2295
2694
  try {
2296
- await fs52.access(metaPath);
2297
- const content = await fs52.readFile(metaPath, "utf-8");
2695
+ await fs62.access(metaPath);
2696
+ const content = await fs62.readFile(metaPath, "utf-8");
2298
2697
  const parsed = JSON.parse(content);
2299
2698
  const meta = collectionMetaSchema.parse(parsed);
2300
2699
  collections.push({
@@ -2326,7 +2725,7 @@ async function loadCollectionMetaFiles(contentRoot) {
2326
2725
  let root = null;
2327
2726
  const rootMetaPath = join3(contentRoot, ".collection.json");
2328
2727
  try {
2329
- await fs52.access(rootMetaPath);
2728
+ await fs62.access(rootMetaPath);
2330
2729
  } catch (err) {
2331
2730
  if (err.code === "ENOENT") {
2332
2731
  } else {
@@ -2334,7 +2733,7 @@ async function loadCollectionMetaFiles(contentRoot) {
2334
2733
  }
2335
2734
  }
2336
2735
  try {
2337
- const content = await fs52.readFile(rootMetaPath, "utf-8");
2736
+ const content = await fs62.readFile(rootMetaPath, "utf-8");
2338
2737
  const parsed = JSON.parse(content);
2339
2738
  root = rootCollectionMetaSchema.parse(parsed);
2340
2739
  } catch (err) {
@@ -2424,16 +2823,16 @@ function isValidSchema(schema) {
2424
2823
  async function isStaleByMtime(dir, cachedAt) {
2425
2824
  let entries;
2426
2825
  try {
2427
- entries = await fs62.readdir(dir, { recursive: true, encoding: "utf-8" });
2826
+ entries = await fs72.readdir(dir, { recursive: true, encoding: "utf-8" });
2428
2827
  } catch {
2429
2828
  return true;
2430
2829
  }
2431
2830
  for (const entry of entries) {
2432
2831
  if (!entry.endsWith(".collection.json"))
2433
2832
  continue;
2434
- const full = path72.join(dir, entry);
2833
+ const full = path82.join(dir, entry);
2435
2834
  try {
2436
- const stat = await fs62.stat(full);
2835
+ const stat = await fs72.stat(full);
2437
2836
  if (stat.mtimeMs > cachedAt.getTime())
2438
2837
  return true;
2439
2838
  } catch {
@@ -2456,7 +2855,7 @@ async function invalidateBranchContentCaches(root) {
2456
2855
  }
2457
2856
  async function filePathExists3(filePath) {
2458
2857
  try {
2459
- await fs72.stat(filePath);
2858
+ await fs8.stat(filePath);
2460
2859
  return true;
2461
2860
  } catch (err) {
2462
2861
  if (isNotFoundError3(err))
@@ -2464,6 +2863,15 @@ async function filePathExists3(filePath) {
2464
2863
  throw err;
2465
2864
  }
2466
2865
  }
2866
+ async function readFileIfExists(filePath) {
2867
+ try {
2868
+ return await fs8.readFile(filePath, "utf8");
2869
+ } catch (err) {
2870
+ if (isNotFoundError3(err))
2871
+ return void 0;
2872
+ throw err;
2873
+ }
2874
+ }
2467
2875
  function validateAndNormalizePath(root, target) {
2468
2876
  const resolvedRoot = resolve(root);
2469
2877
  const withSep = resolvedRoot.endsWith(sep) ? resolvedRoot : `${resolvedRoot}${sep}`;
@@ -2493,11 +2901,11 @@ function resolveBranchPath(options) {
2493
2901
  const safeBranch = sanitizeBranchName(options.branchName);
2494
2902
  const strategy = operatingStrategy2(options.mode);
2495
2903
  const baseRoot = resolveContentBranchesRoot(options.mode, options.basePathOverride);
2496
- const normalizedBase = path92.resolve(baseRoot);
2497
- const baseWithSep = normalizedBase.endsWith(path92.sep) ? normalizedBase : `${normalizedBase}${path92.sep}`;
2904
+ const normalizedBase = path102.resolve(baseRoot);
2905
+ const baseWithSep = normalizedBase.endsWith(path102.sep) ? normalizedBase : `${normalizedBase}${path102.sep}`;
2498
2906
  const branchRoot = strategy.getContentBranchRoot(safeBranch, options.basePathOverride);
2499
2907
  const withinBase = (target) => {
2500
- const resolved = path92.resolve(target);
2908
+ const resolved = path102.resolve(target);
2501
2909
  return resolved === normalizedBase || resolved.startsWith(baseWithSep);
2502
2910
  };
2503
2911
  if (!withinBase(branchRoot)) {
@@ -2507,7 +2915,7 @@ function resolveBranchPath(options) {
2507
2915
  }
2508
2916
  async function ensureBranchRoot(options) {
2509
2917
  const result = resolveBranchPath(options);
2510
- await fs8.mkdir(result.branchRoot, { recursive: true });
2918
+ await fs9.mkdir(result.branchRoot, { recursive: true });
2511
2919
  return result;
2512
2920
  }
2513
2921
  async function withLocks(keys, fn) {
@@ -2795,7 +3203,7 @@ function renderField(field, value, depth, entry, config) {
2795
3203
  const heading = "#".repeat(Math.min(depth, 6));
2796
3204
  const descriptionLine = "description" in field && field.description ? `
2797
3205
 
2798
- *${field.description}*` : "";
3206
+ _${field.description}_` : "";
2799
3207
  if ("list" in field && field.list && Array.isArray(value)) {
2800
3208
  return renderListField(field, value, depth, label, heading, descriptionLine, entry, config);
2801
3209
  }
@@ -3051,26 +3459,13 @@ function formatInlineValue(field, value) {
3051
3459
  return String(value);
3052
3460
  }
3053
3461
  function yamlValue(value) {
3462
+ if (value === "")
3463
+ return '""';
3054
3464
  if (/[:#{}[\],&*?|>!%@`]/.test(value) || value.includes("\n")) {
3055
3465
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
3056
3466
  }
3057
3467
  return value;
3058
3468
  }
3059
- function computeEntryUrl(collection, slug, contentRoot) {
3060
- const root = trimSlashes(contentRoot);
3061
- let stripped = collection;
3062
- if (root && collection.startsWith(`${root}/`)) {
3063
- stripped = collection.slice(root.length + 1);
3064
- } else if (collection === root) {
3065
- stripped = "";
3066
- }
3067
- const segments = stripped.split("/").filter(Boolean);
3068
- if (slug && slug !== "index") {
3069
- segments.push(slug);
3070
- }
3071
- const path202 = segments.length > 0 ? `/${segments.join("/")}` : "/";
3072
- return path202.toLowerCase();
3073
- }
3074
3469
  function resolveEntryUrl(location, contentRoot) {
3075
3470
  return computeEntryUrl(location.collection ?? "", location.slug ?? "", contentRoot);
3076
3471
  }
@@ -3237,7 +3632,7 @@ async function processCollection(store, collection, flatSchema, contentRoot, con
3237
3632
  const listed = await store.getCollectionEntryPaths(collection.logicalPath);
3238
3633
  const directEntries = listed.filter((e) => e.collection === collection.logicalPath);
3239
3634
  for (const listEntry of directEntries) {
3240
- const entryTypeName = extractEntryTypeFromFilename(path11.basename(listEntry.relativePath));
3635
+ const entryTypeName = extractEntryTypeFromFilename(path12.basename(listEntry.relativePath));
3241
3636
  if (!entryTypeName)
3242
3637
  continue;
3243
3638
  if (config?.exclude?.entryTypes?.includes(entryTypeName))
@@ -3255,7 +3650,7 @@ async function processCollection(store, collection, flatSchema, contentRoot, con
3255
3650
  }
3256
3651
  if (config?.exclude?.where?.(aiEntry))
3257
3652
  continue;
3258
- await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path11.basename(listEntry.relativePath)), config);
3653
+ await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path12.basename(listEntry.relativePath)), config);
3259
3654
  entries.push(aiEntry);
3260
3655
  const entryFilePath = `${cleanPath}/${listEntry.slug}.md`;
3261
3656
  const entryMarkdown = entryToMarkdown(aiEntry, config);
@@ -3306,7 +3701,7 @@ async function processRootEntries(store, rootCollection, contentRoot, config, id
3306
3701
  const listed = await store.getCollectionEntryPaths(rootCollection.logicalPath);
3307
3702
  const directEntries = listed.filter((e) => e.collection === rootCollection.logicalPath);
3308
3703
  for (const listEntry of directEntries) {
3309
- const entryTypeName = extractEntryTypeFromFilename(path11.basename(listEntry.relativePath));
3704
+ const entryTypeName = extractEntryTypeFromFilename(path12.basename(listEntry.relativePath));
3310
3705
  if (!entryTypeName)
3311
3706
  continue;
3312
3707
  if (config?.exclude?.entryTypes?.includes(entryTypeName))
@@ -3324,7 +3719,7 @@ async function processRootEntries(store, rootCollection, contentRoot, config, id
3324
3719
  }
3325
3720
  if (config?.exclude?.where?.(aiEntry))
3326
3721
  continue;
3327
- await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path11.basename(listEntry.relativePath)), config);
3722
+ await runEntryTransform(aiEntry, doc.absolutePath, extractIdFromFilename(path12.basename(listEntry.relativePath)), config);
3328
3723
  entries.push(aiEntry);
3329
3724
  const entryFilePath = `${listEntry.slug}.md`;
3330
3725
  const entryMarkdown = entryToMarkdown(aiEntry, config);
@@ -3372,19 +3767,19 @@ function docToAIEntry(doc, slug, entryTypeName, entryTypeConfig, cleanCollection
3372
3767
  };
3373
3768
  }
3374
3769
  function makeReadSibling(dir) {
3375
- const resolvedDir = path11.resolve(dir);
3770
+ const resolvedDir = path12.resolve(dir);
3376
3771
  return async (name) => {
3377
- if (!name || name.includes("/") || name.includes("\\") || path11.isAbsolute(name) || hasTraversalSequence(name)) {
3772
+ if (!name || name.includes("/") || name.includes("\\") || path12.isAbsolute(name) || hasTraversalSequence(name)) {
3378
3773
  return null;
3379
3774
  }
3380
- const abs = path11.resolve(resolvedDir, name);
3381
- if (path11.dirname(abs) !== resolvedDir)
3775
+ const abs = path12.resolve(resolvedDir, name);
3776
+ if (path12.dirname(abs) !== resolvedDir)
3382
3777
  return null;
3383
3778
  try {
3384
- const stats = await fs10.lstat(abs);
3779
+ const stats = await fs11.lstat(abs);
3385
3780
  if (!stats.isFile())
3386
3781
  return null;
3387
- return await fs10.readFile(abs, "utf8");
3782
+ return await fs11.readFile(abs, "utf8");
3388
3783
  } catch (err) {
3389
3784
  if (isNodeError3(err) && (err.code === "ENOENT" || err.code === "ENOTDIR"))
3390
3785
  return null;
@@ -3398,7 +3793,7 @@ async function runEntryTransform(entry, absolutePath, contentId, config) {
3398
3793
  return;
3399
3794
  const ctx = {
3400
3795
  contentId: contentId ?? "",
3401
- readSibling: makeReadSibling(path11.dirname(absolutePath))
3796
+ readSibling: makeReadSibling(path12.dirname(absolutePath))
3402
3797
  };
3403
3798
  try {
3404
3799
  const appended = await transform(entry, ctx);
@@ -3439,29 +3834,29 @@ async function writeOccJsonFile(filePath, payload, options) {
3439
3834
  const writeId = randomUUID2();
3440
3835
  const data = { ...payload, version: newVersion, writeId };
3441
3836
  const content = JSON.stringify(data, null, 2) + (trailingNewline ? "\n" : "");
3442
- const dir = path13.dirname(filePath);
3443
- await fs12.mkdir(dir, { recursive: true });
3837
+ const dir = path14.dirname(filePath);
3838
+ await fs13.mkdir(dir, { recursive: true });
3444
3839
  const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
3445
- await fs12.writeFile(tempPath, content, "utf-8");
3840
+ await fs13.writeFile(tempPath, content, "utf-8");
3446
3841
  if (expectedVersion === null) {
3447
3842
  try {
3448
- await fs12.link(tempPath, filePath);
3843
+ await fs13.link(tempPath, filePath);
3449
3844
  } catch (err) {
3450
- await fs12.unlink(tempPath).catch(() => {
3845
+ await fs13.unlink(tempPath).catch(() => {
3451
3846
  });
3452
3847
  if (isFileExistsError2(err)) {
3453
3848
  throw new OccWriteConflictError();
3454
3849
  }
3455
3850
  throw err;
3456
3851
  }
3457
- await fs12.unlink(tempPath).catch(() => {
3852
+ await fs13.unlink(tempPath).catch(() => {
3458
3853
  });
3459
3854
  return { version: newVersion, writeId };
3460
3855
  }
3461
3856
  try {
3462
3857
  let currentVersion = null;
3463
3858
  try {
3464
- const current = JSON.parse(await fs12.readFile(filePath, "utf-8"));
3859
+ const current = JSON.parse(await fs13.readFile(filePath, "utf-8"));
3465
3860
  currentVersion = current.version ?? 0;
3466
3861
  } catch {
3467
3862
  currentVersion = null;
@@ -3469,16 +3864,16 @@ async function writeOccJsonFile(filePath, payload, options) {
3469
3864
  if (currentVersion !== expectedVersion) {
3470
3865
  throw new OccWriteConflictError();
3471
3866
  }
3472
- await fs12.rename(tempPath, filePath);
3867
+ await fs13.rename(tempPath, filePath);
3473
3868
  if (settleMs > 0) {
3474
3869
  await new Promise((resolve2) => setTimeout(resolve2, settleMs));
3475
3870
  }
3476
- const afterWrite = JSON.parse(await fs12.readFile(filePath, "utf-8"));
3871
+ const afterWrite = JSON.parse(await fs13.readFile(filePath, "utf-8"));
3477
3872
  if (afterWrite.writeId !== writeId) {
3478
3873
  throw new OccWriteConflictError();
3479
3874
  }
3480
3875
  } catch (err) {
3481
- await fs12.unlink(tempPath).catch(() => {
3876
+ await fs13.unlink(tempPath).catch(() => {
3482
3877
  });
3483
3878
  throw err;
3484
3879
  }
@@ -3504,8 +3899,8 @@ async function withOccRetry(operation, options) {
3504
3899
  throw new Error("Unreachable");
3505
3900
  }
3506
3901
  async function withOccFileLock(filePath, fn) {
3507
- const dir = path13.dirname(filePath);
3508
- await fs12.mkdir(dir, { recursive: true });
3902
+ const dir = path14.dirname(filePath);
3903
+ await fs13.mkdir(dir, { recursive: true });
3509
3904
  const MAX_ATTEMPTS = 14;
3510
3905
  let release = null;
3511
3906
  for (let attempt = 1; release === null; attempt++) {
@@ -3589,11 +3984,11 @@ function gitChildEnv(overrides) {
3589
3984
  return { ...env, ...FORCE_C_LOCALE, ...overrides };
3590
3985
  }
3591
3986
  async function ensureGitExcludePattern(repoPath, pattern) {
3592
- const excludePath = path15.join(repoPath, ".git", "info", "exclude");
3593
- await fs14.mkdir(path15.dirname(excludePath), { recursive: true });
3987
+ const excludePath = path16.join(repoPath, ".git", "info", "exclude");
3988
+ await fs15.mkdir(path16.dirname(excludePath), { recursive: true });
3594
3989
  let content = "";
3595
3990
  try {
3596
- content = await fs14.readFile(excludePath, "utf-8");
3991
+ content = await fs15.readFile(excludePath, "utf-8");
3597
3992
  } catch (err) {
3598
3993
  if (!isNotFoundError3(err))
3599
3994
  throw err;
@@ -3605,7 +4000,7 @@ async function ensureGitExcludePattern(repoPath, pattern) {
3605
4000
  }
3606
4001
  const needsLeadingNewline = content.length > 0 && !content.endsWith("\n");
3607
4002
  const newContent = content + (needsLeadingNewline ? "\n" : "") + pattern + "\n";
3608
- await fs14.writeFile(excludePath, newContent, "utf-8");
4003
+ await fs15.writeFile(excludePath, newContent, "utf-8");
3609
4004
  log5.debug("git", "Added pattern to .git/info/exclude", { pattern });
3610
4005
  }
3611
4006
  async function loadOrCreateBranchContext(options) {
@@ -3669,10 +4064,12 @@ async function listEntries(branchRoot, flatSchema, contentRootName, options, vis
3669
4064
  const collections = flatSchema.filter((item) => item.type === "collection" && item.entries !== void 0 && (item.logicalPath === rootPath || item.logicalPath.startsWith(`${rootPath}/`)));
3670
4065
  const shouldInclude = visibility?.shouldInclude;
3671
4066
  const skippedFiles = [];
4067
+ const resolver = options?.resolveReferences ? createReferenceResolver(branchRoot, flatSchema, contentRootName) : null;
3672
4068
  const collectionResults = await Promise.all(collections.map(async (collection) => {
3673
4069
  const entries = await listCollectionEntries(branchRoot, collection, (file) => skippedFiles.push(file));
3674
4070
  const visible = shouldInclude ? entries.filter((e) => shouldInclude(e.physicalPath)) : entries;
3675
- return visible.map((entry) => ({ entry, collection }));
4071
+ const resolved = resolver ? await resolveCollectionItemReferences(visible, collection, resolver) : visible;
4072
+ return resolved.map((entry) => ({ entry, collection }));
3676
4073
  }));
3677
4074
  if (isBuildMode() && skippedFiles.length > 0) {
3678
4075
  const lines = skippedFiles.map(({ filename, collectionPath }) => ` - ${collectionPath}/${filename}`);
@@ -3686,8 +4083,7 @@ Expected {type}.{slug}.{id}.{ext} with a known entry type (a name declared in th
3686
4083
  for (const { entry, collection } of results) {
3687
4084
  const pathWithoutRoot = entry.logicalPath.startsWith(contentPrefix) ? entry.logicalPath.slice(contentPrefix.length) : entry.logicalPath;
3688
4085
  const pathSegments = pathWithoutRoot.split("/").filter(Boolean);
3689
- const urlSegments = entry.slug === "index" ? pathSegments.slice(0, -1) : pathSegments;
3690
- const urlPath = urlSegments.length > 0 ? `/${urlSegments.join("/")}`.toLowerCase() : "/";
4086
+ const urlPath = computeEntryUrl(entry.collectionPath, entry.slug, contentRootName);
3691
4087
  const raw = entry.data;
3692
4088
  const meta = {
3693
4089
  entryPath: entry.logicalPath,
@@ -3732,7 +4128,7 @@ function isValidCropRect(x, y, w, h) {
3732
4128
  return false;
3733
4129
  return true;
3734
4130
  }
3735
- function isPlainRecord(value) {
4131
+ function isPlainRecord2(value) {
3736
4132
  return typeof value === "object" && value !== null && !Array.isArray(value);
3737
4133
  }
3738
4134
  function resolveBlockItem(blockField, item) {
@@ -3742,17 +4138,17 @@ function resolveBlockItem(blockField, item) {
3742
4138
  const template = blockField.templates?.find((t) => t.name === templateName);
3743
4139
  if (!template?.fields)
3744
4140
  return void 0;
3745
- const data = isPlainRecord(item.value) ? item.value : item;
4141
+ const data = isPlainRecord2(item.value) ? item.value : item;
3746
4142
  return { fields: template.fields, data };
3747
4143
  }
3748
- function isPlainRecord2(value) {
4144
+ function isPlainRecord3(value) {
3749
4145
  return typeof value === "object" && value !== null && !Array.isArray(value);
3750
4146
  }
3751
4147
  function isPositiveInt(value) {
3752
4148
  return typeof value === "number" && Number.isInteger(value) && value > 0;
3753
4149
  }
3754
4150
  function isValidImageCropValue(value) {
3755
- if (!isPlainRecord2(value))
4151
+ if (!isPlainRecord3(value))
3756
4152
  return false;
3757
4153
  const { x, y, w, h } = value;
3758
4154
  if (typeof x !== "number" || typeof y !== "number" || typeof w !== "number" || typeof h !== "number") {
@@ -3760,78 +4156,78 @@ function isValidImageCropValue(value) {
3760
4156
  }
3761
4157
  return isValidCropRect(x, y, w, h);
3762
4158
  }
3763
- function validateImageValue(field, value, path202) {
3764
- if (!isPlainRecord2(value)) {
3765
- return [{ fieldPath: path202, message: "Expected an image object with { src, alt }" }];
4159
+ function validateImageValue(field, value, path212) {
4160
+ if (!isPlainRecord3(value)) {
4161
+ return [{ fieldPath: path212, message: "Expected an image object with { src, alt }" }];
3766
4162
  }
3767
4163
  const errors = [];
3768
4164
  if (typeof value.src !== "string" || value.src.trim() === "") {
3769
- errors.push({ fieldPath: `${path202}.src`, message: "Image src is required" });
4165
+ errors.push({ fieldPath: `${path212}.src`, message: "Image src is required" });
3770
4166
  }
3771
4167
  if (typeof value.alt !== "string") {
3772
- errors.push({ fieldPath: `${path202}.alt`, message: "Image alt text is required" });
4168
+ errors.push({ fieldPath: `${path212}.alt`, message: "Image alt text is required" });
3773
4169
  } else if (value.alt.trim() === "" && field.altOptional !== true) {
3774
- errors.push({ fieldPath: `${path202}.alt`, message: "Image alt text is required" });
4170
+ errors.push({ fieldPath: `${path212}.alt`, message: "Image alt text is required" });
3775
4171
  }
3776
4172
  if (value.width !== void 0 && !isPositiveInt(value.width)) {
3777
- errors.push({ fieldPath: `${path202}.width`, message: "Image width must be a positive integer" });
4173
+ errors.push({ fieldPath: `${path212}.width`, message: "Image width must be a positive integer" });
3778
4174
  }
3779
4175
  if (value.height !== void 0 && !isPositiveInt(value.height)) {
3780
4176
  errors.push({
3781
- fieldPath: `${path202}.height`,
4177
+ fieldPath: `${path212}.height`,
3782
4178
  message: "Image height must be a positive integer"
3783
4179
  });
3784
4180
  }
3785
4181
  if (value.crop !== void 0 && !isValidImageCropValue(value.crop)) {
3786
- errors.push({ fieldPath: `${path202}.crop`, message: "Invalid image crop rect" });
4182
+ errors.push({ fieldPath: `${path212}.crop`, message: "Invalid image crop rect" });
3787
4183
  }
3788
4184
  return errors;
3789
4185
  }
3790
4186
  function referenceValueId(value) {
3791
4187
  if (typeof value === "string")
3792
4188
  return value;
3793
- if (isPlainRecord2(value) && typeof value.id === "string")
4189
+ if (isPlainRecord3(value) && typeof value.id === "string")
3794
4190
  return value.id;
3795
4191
  return void 0;
3796
4192
  }
3797
- function validateScalar(field, value, path202) {
4193
+ function validateScalar(field, value, path212) {
3798
4194
  switch (field.type) {
3799
4195
  case "number":
3800
4196
  if (typeof value !== "number" || !Number.isFinite(value)) {
3801
- return [{ fieldPath: path202, message: "Expected a number" }];
4197
+ return [{ fieldPath: path212, message: "Expected a number" }];
3802
4198
  }
3803
4199
  return [];
3804
4200
  case "boolean":
3805
4201
  if (typeof value !== "boolean") {
3806
- return [{ fieldPath: path202, message: "Expected true or false" }];
4202
+ return [{ fieldPath: path212, message: "Expected true or false" }];
3807
4203
  }
3808
4204
  return [];
3809
4205
  case "datetime":
3810
4206
  if (typeof value !== "string" || value !== "" && Number.isNaN(Date.parse(value))) {
3811
- return [{ fieldPath: path202, message: "Expected a valid date/time string" }];
4207
+ return [{ fieldPath: path212, message: "Expected a valid date/time string" }];
3812
4208
  }
3813
4209
  return [];
3814
4210
  case "select": {
3815
4211
  if (typeof value !== "string") {
3816
- return [{ fieldPath: path202, message: "Expected a selection" }];
4212
+ return [{ fieldPath: path212, message: "Expected a selection" }];
3817
4213
  }
3818
4214
  const options = selectOptionValues(field);
3819
4215
  if (value !== "" && !options.includes(value)) {
3820
- return [{ fieldPath: path202, message: `Must be one of: ${options.join(", ")}` }];
4216
+ return [{ fieldPath: path212, message: `Must be one of: ${options.join(", ")}` }];
3821
4217
  }
3822
4218
  return [];
3823
4219
  }
3824
4220
  case "reference":
3825
4221
  if (referenceValueId(value) === void 0) {
3826
- return [{ fieldPath: path202, message: "Expected a reference id" }];
4222
+ return [{ fieldPath: path212, message: "Expected a reference id" }];
3827
4223
  }
3828
4224
  return [];
3829
4225
  case "image":
3830
- return validateImageValue(field, value, path202);
4226
+ return validateImageValue(field, value, path212);
3831
4227
  default:
3832
4228
  if (STRING_FIELD_TYPES.has(field.type)) {
3833
4229
  if (typeof value !== "string") {
3834
- return [{ fieldPath: path202, message: "Expected text" }];
4230
+ return [{ fieldPath: path212, message: "Expected text" }];
3835
4231
  }
3836
4232
  }
3837
4233
  return [];
@@ -3854,12 +4250,12 @@ function validateEntryData(fields, data, pathPrefix = "") {
3854
4250
  errors.push(...validateEntryData(field.fields, data, pathPrefix));
3855
4251
  continue;
3856
4252
  }
3857
- const path202 = pathPrefix ? `${pathPrefix}.${field.name}` : field.name;
4253
+ const path212 = pathPrefix ? `${pathPrefix}.${field.name}` : field.name;
3858
4254
  const value = data[field.name];
3859
4255
  const isRequired = "required" in field && field.required === true;
3860
4256
  if (isRequired && isEmptyForRequired(field, value)) {
3861
4257
  const message = Array.isArray(value) ? "At least one item is required" : "This field is required";
3862
- errors.push({ fieldPath: path202, message });
4258
+ errors.push({ fieldPath: path212, message });
3863
4259
  continue;
3864
4260
  }
3865
4261
  if (value === void 0 || value === null)
@@ -3867,12 +4263,12 @@ function validateEntryData(fields, data, pathPrefix = "") {
3867
4263
  if (field.type === "block") {
3868
4264
  const blockField = field;
3869
4265
  if (!Array.isArray(value)) {
3870
- errors.push({ fieldPath: path202, message: "Expected a list of blocks" });
4266
+ errors.push({ fieldPath: path212, message: "Expected a list of blocks" });
3871
4267
  continue;
3872
4268
  }
3873
4269
  value.forEach((item, index) => {
3874
- const itemPath = `${path202}[${index}]`;
3875
- if (!isPlainRecord2(item)) {
4270
+ const itemPath = `${path212}[${index}]`;
4271
+ if (!isPlainRecord3(item)) {
3876
4272
  errors.push({ fieldPath: itemPath, message: "Expected a block object" });
3877
4273
  return;
3878
4274
  }
@@ -3893,12 +4289,12 @@ function validateEntryData(fields, data, pathPrefix = "") {
3893
4289
  const objectField = field;
3894
4290
  if (objectField.list) {
3895
4291
  if (!Array.isArray(value)) {
3896
- errors.push({ fieldPath: path202, message: "Expected a list of items" });
4292
+ errors.push({ fieldPath: path212, message: "Expected a list of items" });
3897
4293
  continue;
3898
4294
  }
3899
4295
  value.forEach((item, index) => {
3900
- const itemPath = `${path202}[${index}]`;
3901
- if (!isPlainRecord2(item)) {
4296
+ const itemPath = `${path212}[${index}]`;
4297
+ if (!isPlainRecord3(item)) {
3902
4298
  errors.push({ fieldPath: itemPath, message: "Expected an object" });
3903
4299
  return;
3904
4300
  }
@@ -3906,28 +4302,28 @@ function validateEntryData(fields, data, pathPrefix = "") {
3906
4302
  errors.push(...validateEntryData(objectField.fields, item, itemPath));
3907
4303
  }
3908
4304
  });
3909
- } else if (!isPlainRecord2(value)) {
3910
- errors.push({ fieldPath: path202, message: "Expected an object" });
4305
+ } else if (!isPlainRecord3(value)) {
4306
+ errors.push({ fieldPath: path212, message: "Expected an object" });
3911
4307
  } else if (objectField.fields) {
3912
- errors.push(...validateEntryData(objectField.fields, value, path202));
4308
+ errors.push(...validateEntryData(objectField.fields, value, path212));
3913
4309
  }
3914
4310
  continue;
3915
4311
  }
3916
4312
  if ("list" in field && field.list) {
3917
4313
  if (!Array.isArray(value)) {
3918
- errors.push({ fieldPath: path202, message: "Expected a list of values" });
4314
+ errors.push({ fieldPath: path212, message: "Expected a list of values" });
3919
4315
  continue;
3920
4316
  }
3921
4317
  value.forEach((item, index) => {
3922
- errors.push(...validateScalar(field, item, `${path202}[${index}]`));
4318
+ errors.push(...validateScalar(field, item, `${path212}[${index}]`));
3923
4319
  });
3924
4320
  continue;
3925
4321
  }
3926
4322
  if (Array.isArray(value) && KNOWN_FIELD_TYPES.has(field.type)) {
3927
- errors.push({ fieldPath: path202, message: "Expected a single value, not a list" });
4323
+ errors.push({ fieldPath: path212, message: "Expected a single value, not a list" });
3928
4324
  continue;
3929
4325
  }
3930
- errors.push(...validateScalar(field, value, path202));
4326
+ errors.push(...validateScalar(field, value, path212));
3931
4327
  }
3932
4328
  return errors;
3933
4329
  }
@@ -3971,6 +4367,37 @@ function assertBuildEntriesValid(items, phaseLabel) {
3971
4367
  ${lines.join("\n")}
3972
4368
  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.`);
3973
4369
  }
4370
+ async function readGeneratedRecord(outputDir) {
4371
+ try {
4372
+ const raw = await fs17.readFile(path19.join(outputDir, GENERATED_RECORD_FILENAME), "utf-8");
4373
+ const parsed = JSON.parse(raw);
4374
+ if (typeof parsed !== "object" || parsed === null || !("files" in parsed))
4375
+ return [];
4376
+ const { files } = parsed;
4377
+ if (!Array.isArray(files))
4378
+ return [];
4379
+ return files.filter((entry) => typeof entry === "string");
4380
+ } catch {
4381
+ return [];
4382
+ }
4383
+ }
4384
+ async function writeGeneratedRecord(outputDir, files) {
4385
+ const record = { files: [...files].sort() };
4386
+ await fs17.mkdir(outputDir, { recursive: true });
4387
+ await fs17.writeFile(path19.join(outputDir, GENERATED_RECORD_FILENAME), `${JSON.stringify(record, null, 2)}
4388
+ `, "utf-8");
4389
+ }
4390
+ async function removeEmptyDirsUpward(startDir, stopAt) {
4391
+ let current = startDir;
4392
+ while (current.startsWith(stopAt) && current !== stopAt) {
4393
+ try {
4394
+ await fs17.rmdir(current);
4395
+ } catch {
4396
+ return;
4397
+ }
4398
+ current = path19.dirname(current);
4399
+ }
4400
+ }
3974
4401
  async function generateAIContentFiles(options) {
3975
4402
  const { config, entrySchemaRegistry, outputDir, aiConfig: aiConfig2, _testFlatSchema } = options;
3976
4403
  const contentRootName = config.contentRoot || "content";
@@ -3993,23 +4420,45 @@ async function generateAIContentFiles(options) {
3993
4420
  config: aiConfig2,
3994
4421
  entryLinkUrl: config.entryLinkUrl
3995
4422
  });
3996
- const absoluteOutputDir = path18.resolve(outputDir) + path18.sep;
3997
- let fileCount = 0;
3998
- for (const [filePath, content] of result.files) {
3999
- const absolutePath = path18.resolve(path18.join(absoluteOutputDir, filePath));
4423
+ const absoluteOutputDir = path19.resolve(outputDir) + path19.sep;
4424
+ const resolveOutputPath = (filePath) => {
4425
+ const absolutePath = path19.resolve(path19.join(absoluteOutputDir, filePath));
4000
4426
  if (!absolutePath.startsWith(absoluteOutputDir)) {
4001
4427
  throw new Error(`Path traversal detected in AI content output: ${filePath}`);
4002
4428
  }
4003
- await fs16.mkdir(path18.dirname(absolutePath), { recursive: true });
4004
- await fs16.writeFile(absolutePath, content, "utf-8");
4429
+ return absolutePath;
4430
+ };
4431
+ const previousFiles = await readGeneratedRecord(absoluteOutputDir);
4432
+ const currentFiles = [...result.files.keys()];
4433
+ await writeGeneratedRecord(absoluteOutputDir, [.../* @__PURE__ */ new Set([...previousFiles, ...currentFiles])]);
4434
+ let fileCount = 0;
4435
+ for (const [filePath, content] of result.files) {
4436
+ const absolutePath = resolveOutputPath(filePath);
4437
+ await fs17.mkdir(path19.dirname(absolutePath), { recursive: true });
4438
+ await fs17.writeFile(absolutePath, content, "utf-8");
4005
4439
  fileCount++;
4006
4440
  }
4007
- return { fileCount, outputDir: absoluteOutputDir };
4441
+ const currentSet = new Set(currentFiles);
4442
+ const stale = previousFiles.filter((filePath) => !currentSet.has(filePath));
4443
+ let removedCount = 0;
4444
+ for (const filePath of stale) {
4445
+ const absolutePath = resolveOutputPath(filePath);
4446
+ try {
4447
+ await fs17.unlink(absolutePath);
4448
+ removedCount++;
4449
+ } catch (err) {
4450
+ if (!isNodeError3(err) || err.code !== "ENOENT")
4451
+ throw err;
4452
+ }
4453
+ await removeEmptyDirsUpward(path19.dirname(absolutePath), absoluteOutputDir);
4454
+ }
4455
+ await writeGeneratedRecord(absoluteOutputDir, currentFiles);
4456
+ return { fileCount, removedCount, outputDir: absoluteOutputDir };
4008
4457
  }
4009
4458
  async function generateAIContentCLI(options) {
4010
4459
  const { projectDir, outputDir = "public/ai", configPath, appDir = "app" } = options;
4011
4460
  console.log("\nCanopyCMS generate-ai-content\n");
4012
- const canopyConfigPath = path19.join(projectDir, "canopycms.config.ts");
4461
+ const canopyConfigPath = path20.join(projectDir, "canopycms.config.ts");
4013
4462
  let canopyConfigModule;
4014
4463
  try {
4015
4464
  canopyConfigModule = await jiti.import(canopyConfigPath);
@@ -4020,7 +4469,7 @@ async function generateAIContentCLI(options) {
4020
4469
  }
4021
4470
  const configExport = canopyConfigModule.default ?? canopyConfigModule.config ?? canopyConfigModule;
4022
4471
  const serverConfig = typeof configExport === "object" && configExport !== null && "server" in configExport ? configExport.server : configExport;
4023
- const schemasPath = path19.join(projectDir, appDir, "schemas.ts");
4472
+ const schemasPath = path20.join(projectDir, appDir, "schemas.ts");
4024
4473
  let entrySchemaRegistry = {};
4025
4474
  try {
4026
4475
  const schemasModule = await jiti.import(schemasPath);
@@ -4031,7 +4480,7 @@ async function generateAIContentCLI(options) {
4031
4480
  let aiConfig2;
4032
4481
  if (configPath) {
4033
4482
  try {
4034
- const aiConfigModule = await jiti.import(path19.resolve(configPath));
4483
+ const aiConfigModule = await jiti.import(path20.resolve(configPath));
4035
4484
  aiConfig2 = aiConfigModule.aiContentConfig ?? aiConfigModule.default ?? aiConfigModule.config;
4036
4485
  } catch (err) {
4037
4486
  console.error(`Could not load AI config from ${configPath}`);
@@ -4048,7 +4497,7 @@ async function generateAIContentCLI(options) {
4048
4497
  console.error("Make sure canopycms.config.ts uses defineCanopyConfig().");
4049
4498
  process.exit(1);
4050
4499
  }
4051
- const resolvedOutput = path19.resolve(projectDir, outputDir);
4500
+ const resolvedOutput = path20.resolve(projectDir, outputDir);
4052
4501
  console.log(` Output: ${resolvedOutput}`);
4053
4502
  console.log(` Mode: ${serverConfig.mode ?? "dev"}`);
4054
4503
  const result = await generateAIContentFiles({
@@ -4059,10 +4508,13 @@ async function generateAIContentCLI(options) {
4059
4508
  });
4060
4509
  console.log(`
4061
4510
  Generated ${result.fileCount} files`);
4511
+ if (result.removedCount > 0) {
4512
+ console.log(` Removed ${result.removedCount} stale files from the previous run`);
4513
+ }
4062
4514
  console.log(` Output: ${result.outputDir}
4063
4515
  `);
4064
4516
  }
4065
- 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, looksLikeMalformedEntry, listCollectionEntries, MAX_INPUT_PIXELS, KNOWN_FIELD_TYPES, STRING_FIELD_TYPES, selectOptionValues, jiti;
4517
+ 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;
4066
4518
  var init_generate_ai_content = __esm({
4067
4519
  "dist/cli/generate-ai-content.js"() {
4068
4520
  "use strict";
@@ -4075,17 +4527,17 @@ var init_generate_ai_content = __esm({
4075
4527
  for (var name in all)
4076
4528
  __defProp3(target, name, { get: all[name], enumerable: true });
4077
4529
  };
4078
- init_normalize2 = __esm3({
4079
- "dist/paths/normalize.js"() {
4080
- "use strict";
4081
- }
4082
- });
4083
4530
  init_logger2 = __esm3({
4084
4531
  "dist/utils/logger.js"() {
4085
4532
  "use strict";
4086
4533
  active2 = console;
4087
4534
  }
4088
4535
  });
4536
+ init_normalize2 = __esm3({
4537
+ "dist/paths/normalize.js"() {
4538
+ "use strict";
4539
+ }
4540
+ });
4089
4541
  init_types3 = __esm3({
4090
4542
  "dist/paths/types.js"() {
4091
4543
  "use strict";
@@ -4259,7 +4711,12 @@ var init_generate_ai_content = __esm({
4259
4711
  collections: z22.array(z22.string().min(1)).min(1).optional(),
4260
4712
  entryTypes: z22.array(z22.string().min(1)).min(1).optional(),
4261
4713
  displayField: z22.string().min(1).optional(),
4262
- options: z22.array(referenceOptionSchema2).optional()
4714
+ options: z22.array(referenceOptionSchema2).optional(),
4715
+ // Must be listed here, not just on ReferenceFieldConfig: zod strips unknown keys by
4716
+ // default, so a consumer that adopts this schema's parse output would silently delete a
4717
+ // runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
4718
+ // absent on purpose — it is type-inference-only and stripping it changes no behavior.)
4719
+ includeBody: z22.boolean().optional()
4263
4720
  });
4264
4721
  fieldHolder2 = [z22.never()];
4265
4722
  blockSchema2 = z22.object({
@@ -4380,6 +4837,9 @@ var init_generate_ai_content = __esm({
4380
4837
  deployedAsSchema2 = z52.enum(["static", "server"]).default("server");
4381
4838
  contentRootSchema2 = relativePathSchema2.default("content");
4382
4839
  sourceRootSchema2 = z52.string().min(1).optional();
4840
+ basePathSchema2 = z52.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
4841
+ message: "basePath must not be empty or only whitespace"
4842
+ }).optional();
4383
4843
  deploymentNameSchema2 = z52.string().default("prod");
4384
4844
  devContentSyncSchema2 = z52.enum(["off", "warn"]).default("warn");
4385
4845
  devConfigSchema2 = z52.object({
@@ -4442,6 +4902,7 @@ var init_generate_ai_content = __esm({
4442
4902
  deploymentName: deploymentNameSchema2.optional(),
4443
4903
  contentRoot: contentRootSchema2.default("content"),
4444
4904
  sourceRoot: sourceRootSchema2.optional(),
4905
+ basePath: basePathSchema2,
4445
4906
  editor: editorConfigSchema2.optional(),
4446
4907
  authPlugin: z52.custom().optional(),
4447
4908
  entryLinkUrl: z52.custom().optional(),
@@ -4611,32 +5072,32 @@ var init_generate_ai_content = __esm({
4611
5072
  // - shouldCommit(), shouldPush()
4612
5073
  // Add client-unsafe methods (use Node.js APIs)
4613
5074
  getWorkspaceRoot(_sourceRoot) {
4614
- return path82.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE2);
5075
+ return path9.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE2);
4615
5076
  }
4616
5077
  getContentRoot(contentRoot, sourceRoot) {
4617
- return path82.resolve(sourceRoot ?? process.cwd(), contentRoot);
5078
+ return path9.resolve(sourceRoot ?? process.cwd(), contentRoot);
4618
5079
  }
4619
5080
  getContentBranchesRoot(sourceRoot) {
4620
- return path82.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
5081
+ return path9.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
4621
5082
  }
4622
5083
  getContentBranchRoot(branchName, sourceRoot) {
4623
- return path82.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
5084
+ return path9.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
4624
5085
  }
4625
5086
  getGitExcludePattern() {
4626
5087
  return ".canopy-meta/";
4627
5088
  }
4628
5089
  getPermissionsFilePath(root) {
4629
- return path82.join(root, this.getPermissionsFileName());
5090
+ return path9.join(root, this.getPermissionsFileName());
4630
5091
  }
4631
5092
  getGroupsFilePath(root) {
4632
- return path82.join(root, this.getGroupsFileName());
5093
+ return path9.join(root, this.getGroupsFileName());
4633
5094
  }
4634
5095
  getRemoteUrlConfig() {
4635
5096
  return {
4636
5097
  shouldAutoInitLocal: false,
4637
5098
  defaultRemotePath: "",
4638
5099
  envVarName: "CANOPYCMS_REMOTE_URL",
4639
- autoDetectRemotePath: path82.join(this.getWorkspaceRoot(), "remote.git")
5100
+ autoDetectRemotePath: path9.join(this.getWorkspaceRoot(), "remote.git")
4640
5101
  };
4641
5102
  }
4642
5103
  requiresExistingRepo() {
@@ -4648,7 +5109,7 @@ var init_generate_ai_content = __esm({
4648
5109
  return `canopycms-settings-${resolveDeploymentName2(config, "prod")}`;
4649
5110
  }
4650
5111
  getSettingsRoot(sourceRoot) {
4651
- return path82.join(this.getWorkspaceRoot(sourceRoot), "settings");
5112
+ return path9.join(this.getWorkspaceRoot(sourceRoot), "settings");
4652
5113
  }
4653
5114
  usesSeparateSettingsBranch() {
4654
5115
  return true;
@@ -4665,25 +5126,25 @@ var init_generate_ai_content = __esm({
4665
5126
  DevStrategy2 = class extends DevClientSafeStrategy2 {
4666
5127
  // Inherits client-safe methods from DevClientSafeStrategy
4667
5128
  getWorkspaceRoot(sourceRoot) {
4668
- return path82.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
5129
+ return path9.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
4669
5130
  }
4670
5131
  getContentRoot(contentRoot, sourceRoot) {
4671
- return path82.resolve(sourceRoot ?? process.cwd(), contentRoot);
5132
+ return path9.resolve(sourceRoot ?? process.cwd(), contentRoot);
4672
5133
  }
4673
5134
  getContentBranchesRoot(sourceRoot) {
4674
- return path82.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
5135
+ return path9.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
4675
5136
  }
4676
5137
  getContentBranchRoot(branchName, sourceRoot) {
4677
- return path82.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
5138
+ return path9.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
4678
5139
  }
4679
5140
  getGitExcludePattern() {
4680
5141
  return ".canopy-meta/";
4681
5142
  }
4682
5143
  getPermissionsFilePath(root) {
4683
- return path82.join(root, this.getPermissionsFileName());
5144
+ return path9.join(root, this.getPermissionsFileName());
4684
5145
  }
4685
5146
  getGroupsFilePath(root) {
4686
- return path82.join(root, this.getGroupsFileName());
5147
+ return path9.join(root, this.getGroupsFileName());
4687
5148
  }
4688
5149
  getRemoteUrlConfig() {
4689
5150
  return {
@@ -4701,7 +5162,7 @@ var init_generate_ai_content = __esm({
4701
5162
  return `canopycms-settings-${resolveDeploymentName2(config, "local")}`;
4702
5163
  }
4703
5164
  getSettingsRoot(sourceRoot) {
4704
- return path82.join(this.getWorkspaceRoot(sourceRoot), "settings");
5165
+ return path9.join(this.getWorkspaceRoot(sourceRoot), "settings");
4705
5166
  }
4706
5167
  usesSeparateSettingsBranch() {
4707
5168
  return true;
@@ -4736,7 +5197,12 @@ var init_generate_ai_content = __esm({
4736
5197
  init_mode_env2();
4737
5198
  }
4738
5199
  });
5200
+ BLOCK_DISCRIMINATOR_KEYS = ["template", "_type"];
5201
+ BLOCK_STRUCTURAL_KEYS = new Set(BLOCK_DISCRIMINATOR_KEYS);
5202
+ EVIDENCE_MAX_DEPTH = 6;
4739
5203
  locks = /* @__PURE__ */ new Map();
5204
+ init_logger2();
5205
+ init_logger2();
4740
5206
  META_DIR = ".canopy-meta";
4741
5207
  CONTENT_WRITE_LOCK_NAME = "content-write.lock";
4742
5208
  DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS = 2e3;
@@ -4749,6 +5215,67 @@ var init_generate_ai_content = __esm({
4749
5215
  sleep = (ms) => new Promise((resolve2) => {
4750
5216
  setTimeout(resolve2, ms);
4751
5217
  });
5218
+ DEFAULT_SEO_FIELD_NAMES = {
5219
+ title: "metaTitle",
5220
+ description: "metaDescription",
5221
+ ogImage: "ogImage",
5222
+ ogType: "ogType",
5223
+ canonical: "canonical",
5224
+ noindex: "noindex",
5225
+ twitterCard: "twitterCard"
5226
+ };
5227
+ SEO_GROUP_FIELDS = [
5228
+ {
5229
+ name: DEFAULT_SEO_FIELD_NAMES.title,
5230
+ type: "string",
5231
+ label: "Meta Title",
5232
+ description: "Overrides the page title in search results and social cards.",
5233
+ required: false
5234
+ },
5235
+ {
5236
+ name: DEFAULT_SEO_FIELD_NAMES.description,
5237
+ type: "string",
5238
+ label: "Meta Description",
5239
+ description: "Summary shown under the title in search results.",
5240
+ required: false
5241
+ },
5242
+ {
5243
+ name: DEFAULT_SEO_FIELD_NAMES.ogImage,
5244
+ type: "string",
5245
+ label: "Social Image URL",
5246
+ description: "Image for social cards. Site-relative or absolute.",
5247
+ required: false
5248
+ },
5249
+ {
5250
+ name: DEFAULT_SEO_FIELD_NAMES.ogType,
5251
+ type: "select",
5252
+ label: "OpenGraph Type",
5253
+ options: ["website", "article", "profile"],
5254
+ required: false
5255
+ },
5256
+ {
5257
+ name: DEFAULT_SEO_FIELD_NAMES.canonical,
5258
+ type: "string",
5259
+ label: "Canonical URL",
5260
+ description: "Set only to point at a different canonical copy of this page.",
5261
+ required: false
5262
+ },
5263
+ {
5264
+ name: DEFAULT_SEO_FIELD_NAMES.noindex,
5265
+ type: "boolean",
5266
+ label: "Hide from search engines",
5267
+ description: "Marks the page noindex AND drops it from the sitemap.",
5268
+ required: false
5269
+ },
5270
+ {
5271
+ name: DEFAULT_SEO_FIELD_NAMES.twitterCard,
5272
+ type: "select",
5273
+ label: "Twitter Card",
5274
+ options: ["summary", "summary_large_image"],
5275
+ required: false
5276
+ }
5277
+ ];
5278
+ init_normalize2();
4752
5279
  init_normalize2();
4753
5280
  BASE58_PATTERN = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]";
4754
5281
  CONTENT_ID_PATTERN = new RegExp(`^${BASE58_PATTERN}{12}$`);
@@ -5066,6 +5593,31 @@ var init_generate_ai_content = __esm({
5066
5593
  this.pathToId.set(newRelativePath, id);
5067
5594
  }
5068
5595
  };
5596
+ parseTypedFilename = (filename, entryTypes) => {
5597
+ if (filename.startsWith("."))
5598
+ return null;
5599
+ const lastDot = filename.lastIndexOf(".");
5600
+ if (lastDot === -1)
5601
+ return null;
5602
+ const nameWithoutExt = filename.slice(0, lastDot);
5603
+ const parts = nameWithoutExt.split(".");
5604
+ if (parts.length < 3)
5605
+ return null;
5606
+ const potentialType = parts[0];
5607
+ if (entryTypes && !entryTypes.some((e) => e.name === potentialType)) {
5608
+ return null;
5609
+ }
5610
+ const id = parts[parts.length - 1];
5611
+ if (!isValidId(id))
5612
+ return null;
5613
+ const slug = parts.slice(1, -1).join(".").toLowerCase();
5614
+ return {
5615
+ type: potentialType,
5616
+ slug,
5617
+ id
5618
+ };
5619
+ };
5620
+ CONTENT_EXTENSIONS = [".md", ".mdx", ".json", ".yaml"];
5069
5621
  registry = /* @__PURE__ */ new Map();
5070
5622
  finalization = new FinalizationRegistry(({ rootKey, ref }) => {
5071
5623
  const refs = registry.get(rootKey);
@@ -5303,7 +5855,7 @@ var init_generate_ai_content = __esm({
5303
5855
  * the project root, regardless of which entrypoint produced the cwd branchRoot.
5304
5856
  */
5305
5857
  skipDiskCache(branchRoot) {
5306
- return isBuildMode() || path72.resolve(branchRoot) === path72.resolve(process.cwd());
5858
+ return isBuildMode() || path82.resolve(branchRoot) === path82.resolve(process.cwd());
5307
5859
  }
5308
5860
  /**
5309
5861
  * Get schema for a branch (loads from cache or resolves fresh).
@@ -5329,14 +5881,14 @@ var init_generate_ai_content = __esm({
5329
5881
  * Load schema from cache or resolve fresh if cache is missing or stale.
5330
5882
  */
5331
5883
  async loadFromCacheOrResolve(branchRoot, entrySchemaRegistry, contentRootName) {
5332
- const contentRoot = path72.join(branchRoot, contentRootName);
5884
+ const contentRoot = path82.join(branchRoot, contentRootName);
5333
5885
  const skipDiskCache = this.skipDiskCache(branchRoot);
5334
5886
  if (!skipDiskCache) {
5335
- const cacheDir = path72.join(branchRoot, ".canopy-meta");
5336
- const cachePath = path72.join(cacheDir, "schema-cache.json");
5887
+ const cacheDir = path82.join(branchRoot, ".canopy-meta");
5888
+ const cachePath = path82.join(cacheDir, "schema-cache.json");
5337
5889
  let cacheData = null;
5338
5890
  try {
5339
- const cacheContent = await fs62.readFile(cachePath, "utf-8");
5891
+ const cacheContent = await fs72.readFile(cachePath, "utf-8");
5340
5892
  cacheData = JSON.parse(cacheContent);
5341
5893
  } catch {
5342
5894
  cacheData = null;
@@ -5373,7 +5925,7 @@ var init_generate_ai_content = __esm({
5373
5925
  */
5374
5926
  async resolveFreshAndPersist(branchRoot, entrySchemaRegistry, contentRootName, options) {
5375
5927
  const { skipDiskCache } = options;
5376
- const contentRoot = path72.join(branchRoot, contentRootName);
5928
+ const contentRoot = path82.join(branchRoot, contentRootName);
5377
5929
  const read = skipDiskCache ? null : await readResourceGeneration(branchRoot, SCHEMA_GENERATION_RESOURCE);
5378
5930
  const result = await this.resolveFresh(contentRoot, entrySchemaRegistry);
5379
5931
  if (!isValidSchema(result.schema)) {
@@ -5386,9 +5938,9 @@ var init_generate_ai_content = __esm({
5386
5938
  }
5387
5939
  const flatSchema = flattenSchema(result.schema, contentRootName);
5388
5940
  if (!skipDiskCache) {
5389
- const cacheDir = path72.join(branchRoot, ".canopy-meta");
5390
- const cachePath = path72.join(cacheDir, "schema-cache.json");
5391
- await fs62.unlink(path72.join(cacheDir, "schema-cache.stale")).catch(() => {
5941
+ const cacheDir = path82.join(branchRoot, ".canopy-meta");
5942
+ const cachePath = path82.join(cacheDir, "schema-cache.json");
5943
+ await fs72.unlink(path82.join(cacheDir, "schema-cache.stale")).catch(() => {
5392
5944
  });
5393
5945
  if (read && read.ok) {
5394
5946
  const newCache = {
@@ -5398,13 +5950,13 @@ var init_generate_ai_content = __esm({
5398
5950
  cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
5399
5951
  generation: read.token
5400
5952
  };
5401
- await fs62.mkdir(cacheDir, { recursive: true });
5402
- const tmpPath = path72.join(cacheDir, `schema-cache.tmp.${Date.now()}.${Math.random()}.json`);
5403
- await fs62.writeFile(tmpPath, JSON.stringify(newCache, null, 2), "utf-8");
5953
+ await fs72.mkdir(cacheDir, { recursive: true });
5954
+ const tmpPath = path82.join(cacheDir, `schema-cache.tmp.${Date.now()}.${Math.random()}.json`);
5955
+ await fs72.writeFile(tmpPath, JSON.stringify(newCache, null, 2), "utf-8");
5404
5956
  try {
5405
- await fs62.rename(tmpPath, cachePath);
5957
+ await fs72.rename(tmpPath, cachePath);
5406
5958
  } catch (err) {
5407
- await fs62.unlink(tmpPath).catch(() => {
5959
+ await fs72.unlink(tmpPath).catch(() => {
5408
5960
  });
5409
5961
  throw err;
5410
5962
  }
@@ -5514,9 +6066,17 @@ var init_generate_ai_content = __esm({
5514
6066
  this.paths = sorted;
5515
6067
  }
5516
6068
  };
6069
+ UrlPathConflictError = class extends ContentConflictError {
6070
+ constructor(message, conflictingPath) {
6071
+ super(message);
6072
+ this.name = "UrlPathConflictError";
6073
+ this.conflictingPath = conflictingPath;
6074
+ }
6075
+ };
5517
6076
  DEFAULT_INDEX_FRESHNESS_INTERVAL_MS = 1e3;
5518
6077
  FORCED_REFRESH_MIN_INTERVAL_MS = 5e3;
5519
6078
  STALE_LOOKUP = Symbol("stale-index-lookup");
6079
+ createReferenceResolveCache = () => /* @__PURE__ */ new Map();
5520
6080
  ContentStore = class {
5521
6081
  constructor(root, flatSchema, options = {}) {
5522
6082
  this.indexGeneration = 0;
@@ -5525,7 +6085,7 @@ var init_generate_ai_content = __esm({
5525
6085
  this.lastDiskProbeMs = 0;
5526
6086
  this.lastForcedRefreshMs = 0;
5527
6087
  this.indexBuild = null;
5528
- this.root = path10.resolve(root);
6088
+ this.root = path11.resolve(root);
5529
6089
  this.contentRootName = options.contentRootName || "content";
5530
6090
  this.indexFreshnessIntervalMs = options.indexFreshnessIntervalMs ?? DEFAULT_INDEX_FRESHNESS_INTERVAL_MS;
5531
6091
  this.contentWriteLockWaitMs = options.contentWriteLockWaitMs ?? DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS;
@@ -5673,7 +6233,7 @@ var init_generate_ai_content = __esm({
5673
6233
  async findEntryPathsById(dir, id) {
5674
6234
  let entries;
5675
6235
  try {
5676
- entries = await fs9.readdir(dir, { withFileTypes: true });
6236
+ entries = await fs10.readdir(dir, { withFileTypes: true });
5677
6237
  } catch (err) {
5678
6238
  if (isNodeError3(err) && err.code === "ENOENT")
5679
6239
  return [];
@@ -5684,7 +6244,7 @@ var init_generate_ai_content = __esm({
5684
6244
  if (entry.isDirectory())
5685
6245
  continue;
5686
6246
  if (extractIdFromFilename(entry.name) === id) {
5687
- matches.push(path10.relative(this.root, path10.join(dir, entry.name)));
6247
+ matches.push(path11.relative(this.root, path11.join(dir, entry.name)));
5688
6248
  }
5689
6249
  }
5690
6250
  return matches.sort();
@@ -5696,11 +6256,11 @@ var init_generate_ai_content = __esm({
5696
6256
  getSchemaItems() {
5697
6257
  return this.schemaIndex.values();
5698
6258
  }
5699
- assertSchemaItem(path202) {
5700
- const normalized = normalizeFilesystemPath(path202);
6259
+ assertSchemaItem(path212) {
6260
+ const normalized = normalizeFilesystemPath(path212);
5701
6261
  const item = this.schemaIndex.get(normalized);
5702
6262
  if (!item) {
5703
- throw new ContentStoreError(`Unknown schema item: ${path202}`, "NO_SCHEMA_ITEM");
6263
+ throw new ContentStoreError(`Unknown schema item: ${path212}`, "NO_SCHEMA_ITEM");
5704
6264
  }
5705
6265
  return item;
5706
6266
  }
@@ -5767,6 +6327,26 @@ var init_generate_ai_content = __esm({
5767
6327
  return this.createLockKey(schemaItem, slug);
5768
6328
  return prePass.id ? this.idLockKey(prePass.id) : prePass.absolutePath;
5769
6329
  }
6330
+ /**
6331
+ * Refuse a create/rename that would give a second entry a `urlPath` another entry already
6332
+ * holds. See url-collision.ts for the two shapes that count, and the legitimate
6333
+ * landing-page-beside-a-collection shape that deliberately does not.
6334
+ *
6335
+ * @param collectionDir Absolute physical directory the entry will live in.
6336
+ * @param slug The entry's slug, as it will be written.
6337
+ */
6338
+ async assertUrlPathAvailable(collectionDir, slug) {
6339
+ const claimant = await findUrlPathClaimant({
6340
+ collectionDir,
6341
+ slug,
6342
+ contentRoot: path11.resolve(this.root, this.contentRootName)
6343
+ });
6344
+ if (!claimant)
6345
+ return;
6346
+ const relative2 = path11.relative(this.root, claimant.physicalPath);
6347
+ 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.`;
6348
+ throw new UrlPathConflictError(message, claimant.physicalPath);
6349
+ }
5770
6350
  /**
5771
6351
  * Build absolute and relative paths with security validation.
5772
6352
  * All entries use the unified filename pattern: {type}.{slug}.{id}.{ext}
@@ -5783,7 +6363,7 @@ var init_generate_ai_content = __esm({
5783
6363
  * @param options.entryTypeName - For collections with multiple entry types, specify which one to use. Defaults to the default entry type.
5784
6364
  */
5785
6365
  async buildPaths(schemaItem, slug, options = {}) {
5786
- const rootWithSep = this.root.endsWith(path10.sep) ? this.root : `${this.root}${path10.sep}`;
6366
+ const rootWithSep = this.root.endsWith(path11.sep) ? this.root : `${this.root}${path11.sep}`;
5787
6367
  if (schemaItem.type === "entry-type") {
5788
6368
  const parentPath = schemaItem.parentPath || "";
5789
6369
  const parentCollection = this.schemaIndex.get(parentPath);
@@ -5816,7 +6396,7 @@ var init_generate_ai_content = __esm({
5816
6396
  const entryTypeName = entryTypeConfig?.name || "entry";
5817
6397
  let collectionRoot = await resolveCollectionPath(this.root, schemaItem.logicalPath);
5818
6398
  if (!collectionRoot) {
5819
- collectionRoot = path10.resolve(this.root, schemaItem.logicalPath);
6399
+ collectionRoot = path11.resolve(this.root, schemaItem.logicalPath);
5820
6400
  }
5821
6401
  if (!collectionRoot.startsWith(rootWithSep)) {
5822
6402
  throw new ContentStoreError("Path traversal detected", "VALIDATION");
@@ -5826,7 +6406,7 @@ var init_generate_ai_content = __esm({
5826
6406
  let existingEntryType;
5827
6407
  let foundExisting = false;
5828
6408
  if (!id) {
5829
- const entries = await fs9.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
6409
+ const entries = await fs10.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
5830
6410
  const existingFile = entries.find((entry) => {
5831
6411
  if (entry.isDirectory())
5832
6412
  return false;
@@ -5852,14 +6432,14 @@ var init_generate_ai_content = __esm({
5852
6432
  }
5853
6433
  filename = `${finalEntryTypeName}.${safeSlug}.${id}${ext}`;
5854
6434
  }
5855
- const resolved = path10.resolve(collectionRoot, filename);
5856
- const collectionRootWithSep = collectionRoot.endsWith(path10.sep) ? collectionRoot : `${collectionRoot}${path10.sep}`;
6435
+ const resolved = path11.resolve(collectionRoot, filename);
6436
+ const collectionRootWithSep = collectionRoot.endsWith(path11.sep) ? collectionRoot : `${collectionRoot}${path11.sep}`;
5857
6437
  if (!resolved.startsWith(collectionRootWithSep)) {
5858
6438
  throw new ContentStoreError("Path traversal detected", "VALIDATION");
5859
6439
  }
5860
6440
  return {
5861
6441
  absolutePath: resolved,
5862
- relativePath: path10.relative(this.root, resolved),
6442
+ relativePath: path11.relative(this.root, resolved),
5863
6443
  id,
5864
6444
  entryTypeName: finalEntryTypeName,
5865
6445
  existed
@@ -5895,8 +6475,8 @@ var init_generate_ai_content = __esm({
5895
6475
  async read(collectionPath, slug = "", options = {}) {
5896
6476
  const schemaItem = this.assertSchemaItem(collectionPath);
5897
6477
  const { absolutePath, relativePath, entryTypeName: resolvedEntryTypeName } = await this.buildPaths(schemaItem, slug);
5898
- const stat = await fs9.stat(absolutePath);
5899
- const raw = await fs9.readFile(absolutePath, "utf8");
6478
+ const stat = await fs10.stat(absolutePath);
6479
+ const raw = await fs10.readFile(absolutePath, "utf8");
5900
6480
  let doc;
5901
6481
  let format;
5902
6482
  let fields;
@@ -5935,7 +6515,7 @@ var init_generate_ai_content = __esm({
5935
6515
  absolutePath
5936
6516
  };
5937
6517
  } else {
5938
- const parsed = matter(raw);
6518
+ const parsed = matter2(raw);
5939
6519
  doc = {
5940
6520
  collection: schemaItem.logicalPath,
5941
6521
  collectionName: schemaItem.name,
@@ -5998,7 +6578,7 @@ var init_generate_ai_content = __esm({
5998
6578
  const indexedPath = guardIndex.findById(id)?.relativePath ?? null;
5999
6579
  if (indexedPath && indexedPath !== relativePath) {
6000
6580
  const quarantined = guardIndex.getDuplicateFor(id);
6001
- const bothOnDisk = await filePathExists3(absolutePath) && await filePathExists3(path10.join(this.root, indexedPath));
6581
+ const bothOnDisk = await filePathExists3(absolutePath) && await filePathExists3(path11.join(this.root, indexedPath));
6002
6582
  if (quarantined || bothOnDisk) {
6003
6583
  throw new DuplicateContentIdError(id, [
6004
6584
  indexedPath,
@@ -6009,10 +6589,18 @@ var init_generate_ai_content = __esm({
6009
6589
  }
6010
6590
  }
6011
6591
  }
6012
- await fs9.mkdir(path10.dirname(absolutePath), { recursive: true });
6592
+ if (!await filePathExists3(absolutePath)) {
6593
+ const chosenSlug = extractSlugFromFilename(path11.basename(absolutePath));
6594
+ const routable = parseSlug(chosenSlug);
6595
+ if (!routable.ok) {
6596
+ 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");
6597
+ }
6598
+ await this.assertUrlPathAvailable(path11.dirname(absolutePath), chosenSlug);
6599
+ }
6600
+ await fs10.mkdir(path11.dirname(absolutePath), { recursive: true });
6013
6601
  if (input.expectedVersion !== void 0) {
6014
6602
  try {
6015
- const existing = await fs9.stat(absolutePath);
6603
+ const existing = await fs10.stat(absolutePath);
6016
6604
  if (input.expectedVersion === null) {
6017
6605
  throw new ContentConflictError("An entry with this slug already exists");
6018
6606
  }
@@ -6029,7 +6617,7 @@ var init_generate_ai_content = __esm({
6029
6617
  }
6030
6618
  }
6031
6619
  if (existingId && !await filePathExists3(absolutePath)) {
6032
- const actualRelPaths = await this.findEntryPathsById(path10.dirname(absolutePath), existingId);
6620
+ const actualRelPaths = await this.findEntryPathsById(path11.dirname(absolutePath), existingId);
6033
6621
  if (actualRelPaths.length > 1) {
6034
6622
  throw new DuplicateContentIdError(existingId, actualRelPaths);
6035
6623
  }
@@ -6039,14 +6627,15 @@ var init_generate_ai_content = __esm({
6039
6627
  throw new ContentConflictError();
6040
6628
  }
6041
6629
  }
6630
+ const existingRaw = input.format === "json" ? void 0 : await readFileIfExists(absolutePath);
6042
6631
  let content;
6043
6632
  if (input.format === "json") {
6044
6633
  content = `${JSON.stringify(input.data ?? {}, null, 2)}
6045
6634
  `;
6046
6635
  } else if (input.format === "yaml") {
6047
- content = yamlStringify(input.data ?? {});
6636
+ content = serializeYaml(input.data ?? {}, existingRaw);
6048
6637
  } else {
6049
- content = matter.stringify(input.body, input.data ?? {});
6638
+ content = serializeFrontmatter(input.body, input.data ?? {}, existingRaw);
6050
6639
  }
6051
6640
  await atomicWriteFile2(absolutePath, content);
6052
6641
  const liveIndex = this._idIndex;
@@ -6055,7 +6644,7 @@ var init_generate_ai_content = __esm({
6055
6644
  const existing = liveIndex.findById(id);
6056
6645
  if (existing) {
6057
6646
  if (existing.relativePath !== relativePath) {
6058
- staleOldAbsPath = path10.join(this.root, existing.relativePath);
6647
+ staleOldAbsPath = path11.join(this.root, existing.relativePath);
6059
6648
  liveIndex.updatePath(existing.id, relativePath);
6060
6649
  }
6061
6650
  } else {
@@ -6068,13 +6657,13 @@ var init_generate_ai_content = __esm({
6068
6657
  }
6069
6658
  }
6070
6659
  if (staleOldAbsPath) {
6071
- await fs9.unlink(staleOldAbsPath).catch((err) => {
6660
+ await fs10.unlink(staleOldAbsPath).catch((err) => {
6072
6661
  if (!isNodeError3(err) || err.code !== "ENOENT")
6073
6662
  throw err;
6074
6663
  });
6075
6664
  }
6076
6665
  await this.recordOwnMutation(liveIndex);
6077
- const afterStat = await fs9.stat(absolutePath);
6666
+ const afterStat = await fs10.stat(absolutePath);
6078
6667
  const base = {
6079
6668
  collection: schemaItem.logicalPath,
6080
6669
  collectionName: schemaItem.name,
@@ -6152,7 +6741,7 @@ var init_generate_ai_content = __esm({
6152
6741
  const schemaItem = this.assertSchemaItem(collectionPath);
6153
6742
  const { absolutePath } = await this.buildPaths(schemaItem, slug);
6154
6743
  try {
6155
- await fs9.access(absolutePath);
6744
+ await fs10.access(absolutePath);
6156
6745
  return true;
6157
6746
  } catch (err) {
6158
6747
  if (isNodeError3(err) && err.code === "ENOENT")
@@ -6191,7 +6780,7 @@ var init_generate_ai_content = __esm({
6191
6780
  const collectionRoot = await resolveCollectionPath(this.root, collectionPath);
6192
6781
  if (!collectionRoot)
6193
6782
  return 0;
6194
- const entries = await fs9.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
6783
+ const entries = await fs10.readdir(collectionRoot, { withFileTypes: true }).catch(() => []);
6195
6784
  return entries.filter((entry) => !entry.isDirectory() && extractEntryTypeFromFilename(entry.name) === entryTypeName).length;
6196
6785
  }
6197
6786
  /**
@@ -6215,7 +6804,7 @@ var init_generate_ai_content = __esm({
6215
6804
  const collection = this.assertCollection(collectionPath);
6216
6805
  const prePass = await this.buildPaths(collection, slug);
6217
6806
  if (!prePass.existed) {
6218
- await fs9.unlink(prePass.absolutePath);
6807
+ await fs10.unlink(prePass.absolutePath);
6219
6808
  return;
6220
6809
  }
6221
6810
  let lockKey = this.entryLockKey(collection, slug, prePass);
@@ -6231,7 +6820,7 @@ var init_generate_ai_content = __esm({
6231
6820
  return RETRY_KEY;
6232
6821
  }
6233
6822
  const { absolutePath, relativePath } = inLock;
6234
- await fs9.unlink(absolutePath);
6823
+ await fs10.unlink(absolutePath);
6235
6824
  const liveIndex = this._idIndex;
6236
6825
  const id = liveIndex.findByPath(relativePath);
6237
6826
  if (id) {
@@ -6267,6 +6856,10 @@ var init_generate_ai_content = __esm({
6267
6856
  if (currentSlug === safeNewSlug) {
6268
6857
  return { newPath: `${collectionPath}/${currentSlug}` };
6269
6858
  }
6859
+ const routable = parseSlug(safeNewSlug);
6860
+ if (!routable.ok) {
6861
+ 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");
6862
+ }
6270
6863
  const prePass = await this.buildPaths(collection, currentSlug);
6271
6864
  if (!prePass.existed) {
6272
6865
  throw new ContentStoreError(`Entry not found: ${currentSlug}`, "NOT_FOUND");
@@ -6293,7 +6886,7 @@ var init_generate_ai_content = __esm({
6293
6886
  }
6294
6887
  destLockKey = currentDestKey;
6295
6888
  const { absolutePath: currentPath, relativePath: currentRelPath } = inLock;
6296
- const currentFilename = path10.basename(currentPath);
6889
+ const currentFilename = path11.basename(currentPath);
6297
6890
  const parts = currentFilename.split(".");
6298
6891
  if (parts.length < 4) {
6299
6892
  throw new ContentStoreError(`Invalid entry filename format: ${currentFilename}`, "VALIDATION");
@@ -6302,10 +6895,10 @@ var init_generate_ai_content = __esm({
6302
6895
  const contentId = parts[parts.length - 2];
6303
6896
  const ext = `.${parts[parts.length - 1]}`;
6304
6897
  const newFilename = `${entryTypeName}.${safeNewSlug}.${contentId}${ext}`;
6305
- const parentDir = path10.dirname(currentPath);
6306
- const newPath = path10.join(parentDir, newFilename);
6898
+ const parentDir = path11.dirname(currentPath);
6899
+ const newPath = path11.join(parentDir, newFilename);
6307
6900
  try {
6308
- const entries = await fs9.readdir(parentDir, { withFileTypes: true });
6901
+ const entries = await fs10.readdir(parentDir, { withFileTypes: true });
6309
6902
  for (const entry of entries) {
6310
6903
  if (entry.isDirectory())
6311
6904
  continue;
@@ -6318,16 +6911,17 @@ var init_generate_ai_content = __esm({
6318
6911
  if (err instanceof ContentStoreError)
6319
6912
  throw err;
6320
6913
  }
6914
+ await this.assertUrlPathAvailable(parentDir, safeNewSlug);
6321
6915
  try {
6322
- await fs9.link(currentPath, newPath);
6916
+ await fs10.link(currentPath, newPath);
6323
6917
  } catch (err) {
6324
6918
  if (isNodeError3(err) && err.code === "EEXIST") {
6325
6919
  throw new ContentStoreError(`Entry with slug "${safeNewSlug}" already exists in collection "${collectionPath}"`, "VALIDATION");
6326
6920
  }
6327
6921
  throw err;
6328
6922
  }
6329
- await fs9.unlink(currentPath);
6330
- const newRelativePath = path10.relative(this.root, newPath);
6923
+ await fs10.unlink(currentPath);
6924
+ const newRelativePath = path11.relative(this.root, newPath);
6331
6925
  const liveIndex = this._idIndex;
6332
6926
  const entryId = liveIndex.findByPath(currentRelPath);
6333
6927
  if (entryId) {
@@ -6399,34 +6993,57 @@ var init_generate_ai_content = __esm({
6399
6993
  }
6400
6994
  return entries;
6401
6995
  }
6996
+ /**
6997
+ * Resolve every `reference` field in `data` against `fields`, returning a copy.
6998
+ *
6999
+ * This is what `read()` applies automatically (unless `resolveReferences: false`), exposed
7000
+ * so BATCH surfaces can opt into the same resolution without duplicating the walk: the
7001
+ * listing primitives in content-listing.ts and content-tree.ts read entry files straight
7002
+ * off disk and never touch this store, so before this existed a `reference` field reached
7003
+ * `listEntries()`/`buildContentTree()` callers as a bare id string or `null`.
7004
+ *
7005
+ * Pass one {@link ReferenceResolveCache} across a whole batch so a target referenced by
7006
+ * many entries is read once rather than once per referencing entry. Omit `cache` and the
7007
+ * behavior is exactly what `read()` has always done, unmemoized.
7008
+ *
7009
+ * **Path ACLs are not consulted for the targets.** Resolution goes through this store's
7010
+ * own `read()`, below the per-entry permission check in content-reader.ts — so a resolved
7011
+ * target may be an entry the caller could not `read()` directly. That is pre-existing
7012
+ * `read()` behavior, matched here on purpose so one rule covers both; see the
7013
+ * `resolveReferences` option in content-listing.ts for the note adopters see.
7014
+ */
7015
+ async resolveReferences(data, fields, cache) {
7016
+ return this.resolveReferencesInData(data, fields, cache);
7017
+ }
6402
7018
  /**
6403
7019
  * Recursively resolve reference fields in data.
6404
7020
  * This traverses objects, arrays, and blocks to find and resolve all reference fields.
6405
7021
  */
6406
- async resolveReferencesInData(data, fields) {
7022
+ async resolveReferencesInData(data, fields, cache) {
6407
7023
  const resolved = { ...data };
6408
7024
  const idIndex = await this.idIndex();
6409
7025
  for (const field of fields) {
6410
7026
  if (field.type === "group") {
6411
- const groupResolved = await this.resolveReferencesInData(resolved, field.fields);
7027
+ const groupResolved = await this.resolveReferencesInData(resolved, field.fields, cache);
6412
7028
  Object.assign(resolved, groupResolved);
6413
7029
  continue;
6414
7030
  }
6415
7031
  const value = data[field.name];
6416
7032
  if (field.type === "reference") {
7033
+ const includeBody = field.includeBody === true;
6417
7034
  if (typeof value === "string" && value) {
6418
- resolved[field.name] = await this.resolveSingleReference(value, idIndex);
7035
+ resolved[field.name] = await this.resolveSingleReference(value, idIndex, includeBody, cache);
6419
7036
  } else if (field.list && Array.isArray(value)) {
6420
- resolved[field.name] = await Promise.all(value.map((id) => typeof id === "string" ? this.resolveSingleReference(id, idIndex) : null));
7037
+ resolved[field.name] = await Promise.all(value.map((id) => typeof id === "string" ? this.resolveSingleReference(id, idIndex, includeBody, cache) : null));
6421
7038
  }
6422
7039
  } else if (field.type === "object" && value) {
6423
7040
  const objectField = field;
6424
7041
  if (!objectField.fields)
6425
7042
  continue;
6426
7043
  if (objectField.list && Array.isArray(value)) {
6427
- resolved[field.name] = await Promise.all(value.map((item) => typeof item === "object" && item !== null ? this.resolveReferencesInData(item, objectField.fields) : item));
7044
+ resolved[field.name] = await Promise.all(value.map((item) => typeof item === "object" && item !== null ? this.resolveReferencesInData(item, objectField.fields, cache) : item));
6428
7045
  } else if (typeof value === "object") {
6429
- resolved[field.name] = await this.resolveReferencesInData(value, objectField.fields);
7046
+ resolved[field.name] = await this.resolveReferencesInData(value, objectField.fields, cache);
6430
7047
  }
6431
7048
  } else if (field.type === "block" && Array.isArray(value)) {
6432
7049
  const blockField = field;
@@ -6439,7 +7056,7 @@ var init_generate_ai_content = __esm({
6439
7056
  return block;
6440
7057
  return {
6441
7058
  ...b,
6442
- value: await this.resolveReferencesInData(b.value, template.fields)
7059
+ value: await this.resolveReferencesInData(b.value, template.fields, cache)
6443
7060
  };
6444
7061
  }));
6445
7062
  }
@@ -6447,7 +7064,56 @@ var init_generate_ai_content = __esm({
6447
7064
  return resolved;
6448
7065
  }
6449
7066
  /**
6450
- * Resolve a single reference ID to full entry data.
7067
+ * Resolve a single reference ID to full entry data, memoized when a batch cache is
7068
+ * supplied. Without a cache this is a straight pass-through to the uncached path, so
7069
+ * `read()` behaves exactly as it always has.
7070
+ *
7071
+ * Every occurrence gets its OWN deep copy, even on a cache hit. Without that, the memo
7072
+ * would hand one shared object to all 40 entries referencing the same block — a mutation
7073
+ * in one caller's `extract` (truncating a body for a search index, deleting a field) would
7074
+ * silently rewrite it for every sibling, and both `list: true` elements of `[id, id]` would
7075
+ * be the same instance. The copy is what keeps the cache a pure performance optimization
7076
+ * instead of a semantic change.
7077
+ *
7078
+ * Note the uncached path is NOT the clean baseline it looks like. For a json/yaml target it
7079
+ * genuinely reparses fresh per occurrence, but for md/mdx gray-matter serves `data` from a
7080
+ * process-global content-keyed cache, so `resolveSingleReferenceOnce`'s top-level spread
7081
+ * severs exactly one level and NESTED frontmatter objects alias across occurrences, calls and
7082
+ * requests. That is pre-existing `read()` behavior and out of scope here — deliberately, since
7083
+ * touching it would change `read()` — but it means the cached path is the safer of the two,
7084
+ * not a relaxation of a guarantee the uncached one provides. See
7085
+ * `.claude/future-tasks/graymatter-cache-shared-frontmatter.md`.
7086
+ *
7087
+ * What the copy costs, measured rather than assumed (2000 occurrences of one target, local
7088
+ * disk, so every read hits the page cache — the friendliest possible case for NOT caching):
7089
+ * a snippet-sized target is ~63x cheaper to clone than to re-read-and-parse, while a 265KB
7090
+ * JSON target is ~0.8x, i.e. cloning is marginally SLOWER than reparsing it. So the win is
7091
+ * large in the case this exists for and roughly a wash at the pathological end, never a
7092
+ * blow-up. Two things keep the bad end narrow: an md/mdx target resolves to its FRONTMATTER
7093
+ * only *unless the field sets `includeBody`* (`read()` puts the body on `doc.body`, which
7094
+ * `resolveSingleReferenceOnce` spreads in only for an embedding field), so by default body
7095
+ * size is irrelevant no matter how long the document and only a genuinely huge JSON/YAML
7096
+ * target reaches the wash. **`includeBody: true` is the case that CAN reach it on markdown**:
7097
+ * the body then sits inside the memoized object and is cloned once per referencing entry, so
7098
+ * a long document embedded by many pages pays that repeatedly — the reason `includeBody`
7099
+ * exists as an opt-in per field rather than as resolution's default. And in the deployment this
7100
+ * targets, content lives on EFS/NFS where the syscall the memo removes dominates parse and
7101
+ * clone alike, which the local-disk numbers above understate badly. Correctness is the
7102
+ * reason for the copy regardless; the numbers are here so nobody has to re-derive them
7103
+ * before touching this.
7104
+ */
7105
+ resolveSingleReference(id, idIndex, includeBody, cache) {
7106
+ if (!cache)
7107
+ return this.resolveSingleReferenceUncached(id, idIndex, includeBody);
7108
+ const key = includeBody ? `${id}:body` : id;
7109
+ let pending = cache.get(key);
7110
+ if (!pending) {
7111
+ pending = this.resolveSingleReferenceUncached(id, idIndex, includeBody);
7112
+ cache.set(key, pending);
7113
+ }
7114
+ return pending.then((resolved) => resolved === null ? null : structuredClone(resolved));
7115
+ }
7116
+ /**
6451
7117
  * Returns null if the reference is invalid or missing.
6452
7118
  * Includes id, slug, and collection fields for debugging.
6453
7119
  *
@@ -6461,16 +7127,21 @@ var init_generate_ai_content = __esm({
6461
7127
  * idIndex() dedupes concurrent builds — so a sibling call that wins the
6462
7128
  * throttle and rebuilds heals every other miss in the same batch, not just
6463
7129
  * the first.
7130
+ *
7131
+ * A {@link ReferenceResolveCache} sits ABOVE this, never inside it, so every
7132
+ * distinct id still runs the full refresh-and-retry above. What a cache changes
7133
+ * is only that repeats of the SAME id in one batch share that one attempt's
7134
+ * outcome instead of each getting an independent throw of the dice.
6464
7135
  */
6465
- async resolveSingleReference(id, idIndex) {
6466
- const first = await this.resolveSingleReferenceOnce(id, idIndex);
7136
+ async resolveSingleReferenceUncached(id, idIndex, includeBody) {
7137
+ const first = await this.resolveSingleReferenceOnce(id, idIndex, includeBody);
6467
7138
  if (first !== STALE_LOOKUP)
6468
7139
  return first;
6469
7140
  await this.refreshIndexForSuspiciousLookup();
6470
- const second = await this.resolveSingleReferenceOnce(id, await this.idIndex());
7141
+ const second = await this.resolveSingleReferenceOnce(id, await this.idIndex(), includeBody);
6471
7142
  return second === STALE_LOOKUP ? null : second;
6472
7143
  }
6473
- async resolveSingleReferenceOnce(id, idIndex) {
7144
+ async resolveSingleReferenceOnce(id, idIndex, includeBody) {
6474
7145
  try {
6475
7146
  const location = idIndex.findById(id);
6476
7147
  if (!location)
@@ -6481,12 +7152,13 @@ var init_generate_ai_content = __esm({
6481
7152
  const doc = await this.read(location.collection, location.slug, {
6482
7153
  resolveReferences: false
6483
7154
  });
6484
- return {
7155
+ const bodyForEmbed = includeBody && "body" in doc ? { fieldName: doc.bodyFieldName, value: doc.body } : void 0;
7156
+ return buildResolvedReference(doc.data, {
6485
7157
  id,
6486
7158
  slug: location.slug,
6487
7159
  collection: location.collection,
6488
- ...doc.data
6489
- };
7160
+ urlPath: computeEntryUrl(location.collection, location.slug, this.contentRootName)
7161
+ }, bodyForEmbed);
6490
7162
  } catch (error) {
6491
7163
  if (isNodeError3(error) && error.code === "ENOENT")
6492
7164
  return STALE_LOOKUP;
@@ -6511,7 +7183,6 @@ var init_generate_ai_content = __esm({
6511
7183
  "reference",
6512
7184
  "image"
6513
7185
  ]);
6514
- init_normalize2();
6515
7186
  log22 = createDebugLogger2({ prefix: "EntryLinks" });
6516
7187
  BASE58_CHAR = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]";
6517
7188
  ENTRY_LINK_PATTERN = new RegExp(`entry:(${BASE58_CHAR}{12})(#[^\\s)>"']*)?`, "g");
@@ -6527,10 +7198,10 @@ var init_generate_ai_content = __esm({
6527
7198
  constructor(root) {
6528
7199
  this.regenInFlight = null;
6529
7200
  this.lastForcedRefreshMs = 0;
6530
- this.root = path12.resolve(root);
6531
- this.registryPath = path12.join(this.root, REGISTRY_FILE);
6532
- this.stalePath = path12.join(this.root, REGISTRY_STALE_FILE);
6533
- this.tempPath = path12.join(this.root, REGISTRY_TEMP_FILE);
7201
+ this.root = path13.resolve(root);
7202
+ this.registryPath = path13.join(this.root, REGISTRY_FILE);
7203
+ this.stalePath = path13.join(this.root, REGISTRY_STALE_FILE);
7204
+ this.tempPath = path13.join(this.root, REGISTRY_TEMP_FILE);
6534
7205
  }
6535
7206
  /**
6536
7207
  * Returns all branches. Uses the cached snapshot if its embedded generation
@@ -6539,7 +7210,7 @@ var init_generate_ai_content = __esm({
6539
7210
  async list() {
6540
7211
  let parsed;
6541
7212
  try {
6542
- const raw = await fs11.readFile(this.registryPath, "utf8");
7213
+ const raw = await fs12.readFile(this.registryPath, "utf8");
6543
7214
  parsed = JSON.parse(raw);
6544
7215
  } catch (err) {
6545
7216
  if (isNotFoundError3(err)) {
@@ -6628,7 +7299,7 @@ var init_generate_ai_content = __esm({
6628
7299
  async performRegenerate() {
6629
7300
  const read = await readResourceGeneration(this.root, RESOURCE2);
6630
7301
  const branches = await this.scanBranchDirectories();
6631
- await fs11.unlink(this.stalePath).catch(() => {
7302
+ await fs12.unlink(this.stalePath).catch(() => {
6632
7303
  });
6633
7304
  if (!read.ok) {
6634
7305
  return branches;
@@ -6639,12 +7310,12 @@ var init_generate_ai_content = __esm({
6639
7310
  generation: read.token
6640
7311
  };
6641
7312
  const uniqueTempPath = `${this.tempPath}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
6642
- await fs11.mkdir(this.root, { recursive: true });
6643
- await fs11.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
7313
+ await fs12.mkdir(this.root, { recursive: true });
7314
+ await fs12.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
6644
7315
  try {
6645
- await fs11.rename(uniqueTempPath, this.registryPath);
7316
+ await fs12.rename(uniqueTempPath, this.registryPath);
6646
7317
  } catch (err) {
6647
- await fs11.unlink(uniqueTempPath).catch(() => {
7318
+ await fs12.unlink(uniqueTempPath).catch(() => {
6648
7319
  });
6649
7320
  throw err;
6650
7321
  }
@@ -6658,12 +7329,12 @@ var init_generate_ai_content = __esm({
6658
7329
  async scanBranchDirectories() {
6659
7330
  const branches = [];
6660
7331
  try {
6661
- const entries = await fs11.readdir(this.root, { withFileTypes: true });
7332
+ const entries = await fs12.readdir(this.root, { withFileTypes: true });
6662
7333
  for (const entry of entries) {
6663
7334
  if (!entry.isDirectory() || entry.name.startsWith(".")) {
6664
7335
  continue;
6665
7336
  }
6666
- const branchRoot = path12.join(this.root, entry.name);
7337
+ const branchRoot = path13.join(this.root, entry.name);
6667
7338
  let meta;
6668
7339
  try {
6669
7340
  meta = await BranchMetadataFileManager.loadOnly(branchRoot);
@@ -6714,8 +7385,8 @@ var init_generate_ai_content = __esm({
6714
7385
  };
6715
7386
  BranchMetadataFileManager = class _BranchMetadataFileManager {
6716
7387
  constructor(branchRoot, baseRoot, options) {
6717
- this.branchRoot = path14.resolve(branchRoot);
6718
- this.filePath = path14.join(this.branchRoot, BRANCH_META_DIR, BRANCH_META_FILE);
7388
+ this.branchRoot = path15.resolve(branchRoot);
7389
+ this.filePath = path15.join(this.branchRoot, BRANCH_META_DIR, BRANCH_META_FILE);
6719
7390
  this.baseRoot = baseRoot;
6720
7391
  this.settleMs = options?.settleMs;
6721
7392
  }
@@ -6724,10 +7395,10 @@ var init_generate_ai_content = __esm({
6724
7395
  * Use this for read-only access (e.g., in registry scanning or loadBranchContext).
6725
7396
  */
6726
7397
  static async loadOnly(branchRoot) {
6727
- const resolvedRoot = path14.resolve(branchRoot);
6728
- const filePath = path14.join(resolvedRoot, BRANCH_META_DIR, BRANCH_META_FILE);
7398
+ const resolvedRoot = path15.resolve(branchRoot);
7399
+ const filePath = path15.join(resolvedRoot, BRANCH_META_DIR, BRANCH_META_FILE);
6729
7400
  try {
6730
- const raw = await fs13.readFile(filePath, "utf8");
7401
+ const raw = await fs14.readFile(filePath, "utf8");
6731
7402
  return JSON.parse(raw);
6732
7403
  } catch (err) {
6733
7404
  if (isNotFoundError3(err)) {
@@ -6748,7 +7419,7 @@ var init_generate_ai_content = __esm({
6748
7419
  }
6749
7420
  async load() {
6750
7421
  try {
6751
- const raw = await fs13.readFile(this.filePath, "utf8");
7422
+ const raw = await fs14.readFile(this.filePath, "utf8");
6752
7423
  const parsed = JSON.parse(raw);
6753
7424
  const version = parsed.version ?? 0;
6754
7425
  return { meta: parsed, version };
@@ -6810,7 +7481,7 @@ var init_generate_ai_content = __esm({
6810
7481
  */
6811
7482
  async save(incoming) {
6812
7483
  try {
6813
- await fs13.stat(this.branchRoot);
7484
+ await fs14.stat(this.branchRoot);
6814
7485
  } catch (err) {
6815
7486
  if (isNotFoundError3(err)) {
6816
7487
  throw new BranchMetadataConflictError("Branch no longer exists");
@@ -6923,12 +7594,12 @@ var init_generate_ai_content = __esm({
6923
7594
  };
6924
7595
  GitManager = class _GitManager {
6925
7596
  constructor(options, gitOptions) {
6926
- this.repoPath = path15.resolve(options.repoPath);
7597
+ this.repoPath = path16.resolve(options.repoPath);
6927
7598
  this.baseBranch = options.baseBranch ?? "main";
6928
7599
  this.remote = options.remote ?? "origin";
6929
7600
  this.skipIndexMarker = options.skipIndexMarker ?? false;
6930
7601
  this.git = simpleGit2({ baseDir: this.repoPath, ...gitOptions });
6931
- this.git.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path15.dirname(this.repoPath) }));
7602
+ this.git.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path16.dirname(this.repoPath) }));
6932
7603
  }
6933
7604
  static async cloneRepo(remoteUrl, targetPath, baseBranch = "main") {
6934
7605
  log5.debug("git", "Cloning repository", {
@@ -6970,11 +7641,11 @@ var init_generate_ai_content = __esm({
6970
7641
  remotePath: options.remotePath,
6971
7642
  baseBranch: options.baseBranch
6972
7643
  });
6973
- const remoteParent = path15.dirname(options.remotePath);
7644
+ const remoteParent = path16.dirname(options.remotePath);
6974
7645
  releaseLock = await acquireProvisioningLock(remoteParent, ".remote-init.lock");
6975
7646
  let remoteExists = false;
6976
7647
  try {
6977
- const stat = await fs14.stat(options.remotePath);
7648
+ const stat = await fs15.stat(options.remotePath);
6978
7649
  remoteExists = stat.isDirectory();
6979
7650
  } catch (err) {
6980
7651
  if (!isNotFoundError3(err))
@@ -7226,7 +7897,7 @@ var init_generate_ai_content = __esm({
7226
7897
  */
7227
7898
  static async validateGitRepoExists(repoPath) {
7228
7899
  try {
7229
- const stat = await fs14.stat(path15.join(repoPath, ".git"));
7900
+ const stat = await fs15.stat(path16.join(repoPath, ".git"));
7230
7901
  if (!stat.isDirectory()) {
7231
7902
  throw new Error(`Expected git repo at ${repoPath}`);
7232
7903
  }
@@ -7304,7 +7975,7 @@ var init_generate_ai_content = __esm({
7304
7975
  }
7305
7976
  if (config.autoDetectRemotePath) {
7306
7977
  try {
7307
- const stat = await fs14.stat(config.autoDetectRemotePath);
7978
+ const stat = await fs15.stat(config.autoDetectRemotePath);
7308
7979
  if (stat.isDirectory()) {
7309
7980
  log5.debug("git", "Auto-detected local remote", {
7310
7981
  path: config.autoDetectRemotePath
@@ -7317,8 +7988,8 @@ var init_generate_ai_content = __esm({
7317
7988
  if (config.shouldAutoInitLocal) {
7318
7989
  const gitRoot = await this.findGitRoot();
7319
7990
  const sourceRoot = options.sourceRoot;
7320
- const sourcePath = sourceRoot ? path15.resolve(gitRoot, sourceRoot) : gitRoot;
7321
- const localRemotePath = path15.join(sourcePath, config.defaultRemotePath);
7991
+ const sourcePath = sourceRoot ? path16.resolve(gitRoot, sourceRoot) : gitRoot;
7992
+ const localRemotePath = path16.join(sourcePath, config.defaultRemotePath);
7322
7993
  await this.ensureLocalSimulatedRemote({
7323
7994
  remotePath: localRemotePath,
7324
7995
  sourcePath: gitRoot,
@@ -7346,7 +8017,7 @@ var init_generate_ai_content = __esm({
7346
8017
  static async repoExistsAt(workspacePath) {
7347
8018
  try {
7348
8019
  const checkGit = simpleGit2({ baseDir: workspacePath });
7349
- checkGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path15.dirname(workspacePath) }));
8020
+ checkGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path16.dirname(workspacePath) }));
7350
8021
  await checkGit.raw(["rev-parse", "--git-dir"]);
7351
8022
  return true;
7352
8023
  } catch {
@@ -7368,19 +8039,19 @@ var init_generate_ai_content = __esm({
7368
8039
  const baseBranch = await resolveBaseBranch({
7369
8040
  defaultBaseBranch: options.baseBranch,
7370
8041
  mode: options.mode,
7371
- detectFrom: options.sourceRoot ? path15.resolve(process.cwd(), options.sourceRoot) : process.cwd()
8042
+ detectFrom: options.sourceRoot ? path16.resolve(process.cwd(), options.sourceRoot) : process.cwd()
7372
8043
  });
7373
8044
  const remoteName = options.remoteName ?? "origin";
7374
8045
  const repoExists = await _GitManager.repoExistsAt(options.workspacePath);
7375
8046
  if (!repoExists) {
7376
- const gitPath = path15.join(options.workspacePath, ".git");
8047
+ const gitPath = path16.join(options.workspacePath, ".git");
7377
8048
  try {
7378
- const stat = await fs14.stat(gitPath);
8049
+ const stat = await fs15.stat(gitPath);
7379
8050
  if (stat.isDirectory()) {
7380
8051
  log5.debug("git", "Removing corrupt .git directory", {
7381
8052
  workspacePath: options.workspacePath
7382
8053
  });
7383
- await fs14.rm(gitPath, { recursive: true });
8054
+ await fs15.rm(gitPath, { recursive: true });
7384
8055
  }
7385
8056
  } catch (cleanupErr) {
7386
8057
  if (!isNotFoundError3(cleanupErr))
@@ -7407,7 +8078,7 @@ var init_generate_ai_content = __esm({
7407
8078
  }
7408
8079
  justCloned = true;
7409
8080
  const freshGit = simpleGit2({ baseDir: options.workspacePath });
7410
- freshGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path15.dirname(options.workspacePath) }));
8081
+ freshGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path16.dirname(options.workspacePath) }));
7411
8082
  await freshGit.addConfig("canopycms.managed", "true");
7412
8083
  await freshGit.addConfig("user.name", options.gitBotAuthorName);
7413
8084
  await freshGit.addConfig("user.email", options.gitBotAuthorEmail);
@@ -7758,9 +8429,9 @@ var init_generate_ai_content = __esm({
7758
8429
  } catch {
7759
8430
  }
7760
8431
  for (const [filePath, content] of Object.entries(initialFiles)) {
7761
- const absolutePath = path15.join(this.repoPath, filePath);
7762
- await fs14.mkdir(path15.dirname(absolutePath), { recursive: true });
7763
- await fs14.writeFile(absolutePath, content, "utf-8");
8432
+ const absolutePath = path16.join(this.repoPath, filePath);
8433
+ await fs15.mkdir(path16.dirname(absolutePath), { recursive: true });
8434
+ await fs15.writeFile(absolutePath, content, "utf-8");
7764
8435
  await this.git.add(filePath);
7765
8436
  }
7766
8437
  await this.git.commit("Initialize settings branch", ["--allow-empty"]);
@@ -7787,7 +8458,7 @@ var init_generate_ai_content = __esm({
7787
8458
  branchName: options.branchName,
7788
8459
  mode: options.mode
7789
8460
  });
7790
- releaseLock = await acquireProvisioningLock(path16.dirname(options.branchRoot), `.${path16.basename(options.branchRoot)}.init.lock`);
8461
+ releaseLock = await acquireProvisioningLock(path17.dirname(options.branchRoot), `.${path17.basename(options.branchRoot)}.init.lock`);
7791
8462
  await GitManager.initializeWorkspace({
7792
8463
  workspacePath: options.branchRoot,
7793
8464
  branchName: options.branchName,
@@ -7828,7 +8499,7 @@ var init_generate_ai_content = __esm({
7828
8499
  const baseBranch = await resolveBaseBranch({
7829
8500
  defaultBaseBranch: this.config.defaultBaseBranch,
7830
8501
  mode,
7831
- detectFrom: this.config.sourceRoot ? path16.resolve(process.cwd(), this.config.sourceRoot) : void 0
8502
+ detectFrom: this.config.sourceRoot ? path17.resolve(process.cwd(), this.config.sourceRoot) : void 0
7832
8503
  });
7833
8504
  await this.ensureGitWorkspace({
7834
8505
  branchRoot,
@@ -7865,15 +8536,15 @@ var init_generate_ai_content = __esm({
7865
8536
  };
7866
8537
  readEntryData = async (filePath, format, bodyFieldName = "body") => {
7867
8538
  try {
7868
- const raw = await fs15.readFile(filePath, "utf8");
8539
+ const raw = await fs16.readFile(filePath, "utf8");
7869
8540
  if (format === "json") {
7870
8541
  return asRecord(JSON.parse(raw));
7871
8542
  }
7872
8543
  if (format === "yaml") {
7873
8544
  return asRecord(yamlParse2(raw));
7874
8545
  }
7875
- const parsed = matter2(raw);
7876
- const data = parsed.data ?? {};
8546
+ const parsed = matter3(raw);
8547
+ const data = { ...parsed.data ?? {} };
7877
8548
  if (parsed.content) {
7878
8549
  data[bodyFieldName] = parsed.content;
7879
8550
  }
@@ -7885,30 +8556,19 @@ var init_generate_ai_content = __esm({
7885
8556
  return {};
7886
8557
  }
7887
8558
  };
7888
- parseTypedFilename = (filename, entryTypes) => {
7889
- if (filename.startsWith("."))
7890
- return null;
7891
- const lastDot = filename.lastIndexOf(".");
7892
- if (lastDot === -1)
7893
- return null;
7894
- const nameWithoutExt = filename.slice(0, lastDot);
7895
- const parts = nameWithoutExt.split(".");
7896
- if (parts.length < 3)
7897
- return null;
7898
- const potentialType = parts[0];
7899
- if (entryTypes && !entryTypes.some((e) => e.name === potentialType)) {
7900
- return null;
7901
- }
7902
- const id = parts[parts.length - 1];
7903
- if (!isValidId(id))
7904
- return null;
7905
- const slug = parts.slice(1, -1).join(".").toLowerCase();
8559
+ createReferenceResolver = (branchRoot, flatSchema, contentRootName) => ({
8560
+ store: new ContentStore(branchRoot, flatSchema, { contentRootName }),
8561
+ cache: createReferenceResolveCache()
8562
+ });
8563
+ resolveCollectionItemReferences = async (items, collection, resolver) => Promise.all(items.map(async (item) => {
8564
+ const fields = collection.entries?.find((e) => e.name === item.entryType)?.schema;
8565
+ if (!fields)
8566
+ return item;
7906
8567
  return {
7907
- type: potentialType,
7908
- slug,
7909
- id
8568
+ ...item,
8569
+ data: await resolver.store.resolveReferences(item.data, fields, resolver.cache)
7910
8570
  };
7911
- };
8571
+ }));
7912
8572
  looksLikeMalformedEntry = (filename, entryTypes) => {
7913
8573
  if (filename.startsWith(".") || filename.startsWith("_"))
7914
8574
  return false;
@@ -7937,7 +8597,7 @@ var init_generate_ai_content = __esm({
7937
8597
  normalizePath(root, collectionRoot);
7938
8598
  let dirents;
7939
8599
  try {
7940
- dirents = await fs15.readdir(collectionRoot, { withFileTypes: true });
8600
+ dirents = await fs16.readdir(collectionRoot, { withFileTypes: true });
7941
8601
  } catch (err) {
7942
8602
  if (isNotFoundError3(err))
7943
8603
  return [];
@@ -7945,7 +8605,7 @@ var init_generate_ai_content = __esm({
7945
8605
  }
7946
8606
  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));
7947
8607
  const entries = await Promise.all(files.map(async (file) => {
7948
- const absolutePath = path17.join(collectionRoot, file.name);
8608
+ const absolutePath = path18.join(collectionRoot, file.name);
7949
8609
  const relativePath = normalizePath(root, absolutePath);
7950
8610
  const parsed = parseTypedFilename(file.name, entryTypes);
7951
8611
  if (!parsed) {
@@ -7960,7 +8620,7 @@ var init_generate_ai_content = __esm({
7960
8620
  const format = entryType?.format || "json";
7961
8621
  const bodyField = entryType?.schema ? findBodyFieldName(entryType.schema) : "body";
7962
8622
  const [stats, data] = await Promise.all([
7963
- fs15.stat(absolutePath),
8623
+ fs16.stat(absolutePath),
7964
8624
  readEntryData(absolutePath, format, bodyField)
7965
8625
  ]);
7966
8626
  const item = {
@@ -7984,22 +8644,23 @@ var init_generate_ai_content = __esm({
7984
8644
  KNOWN_FIELD_TYPES = new Set(fieldTypes2);
7985
8645
  STRING_FIELD_TYPES = /* @__PURE__ */ new Set(["string", "markdown", "mdx", "code"]);
7986
8646
  selectOptionValues = (field) => (field.options ?? []).map((opt) => typeof opt === "string" ? opt : opt.value);
8647
+ GENERATED_RECORD_FILENAME = ".canopy-generated.json";
7987
8648
  jiti = createJiti2(import.meta.url);
7988
8649
  }
7989
8650
  });
7990
8651
 
7991
8652
  // dist/utils/atomic-write.js
7992
- import fs17 from "node:fs/promises";
7993
- import path20 from "node:path";
8653
+ import fs18 from "node:fs/promises";
8654
+ import path21 from "node:path";
7994
8655
  async function atomicWriteFile3(filePath, content) {
7995
- const dir = path20.dirname(filePath);
7996
- await fs17.mkdir(dir, { recursive: true });
8656
+ const dir = path21.dirname(filePath);
8657
+ await fs18.mkdir(dir, { recursive: true });
7997
8658
  const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
7998
- await fs17.writeFile(tempPath, content, typeof content === "string" ? "utf-8" : void 0);
8659
+ await fs18.writeFile(tempPath, content, typeof content === "string" ? "utf-8" : void 0);
7999
8660
  try {
8000
- await fs17.rename(tempPath, filePath);
8661
+ await fs18.rename(tempPath, filePath);
8001
8662
  } catch (err) {
8002
- await fs17.unlink(tempPath).catch(() => {
8663
+ await fs18.unlink(tempPath).catch(() => {
8003
8664
  });
8004
8665
  throw err;
8005
8666
  }
@@ -8119,11 +8780,11 @@ var init_debug2 = __esm({
8119
8780
  });
8120
8781
 
8121
8782
  // dist/resource-generation.js
8122
- import fs18 from "node:fs/promises";
8123
- import path21 from "node:path";
8783
+ import fs19 from "node:fs/promises";
8784
+ import path23 from "node:path";
8124
8785
  import { randomUUID as randomUUID3 } from "node:crypto";
8125
8786
  function resourceGenerationPath2(root, resource) {
8126
- return path21.resolve(root, META_DIR3, `${resource}.generation`);
8787
+ return path23.resolve(root, META_DIR3, `${resource}.generation`);
8127
8788
  }
8128
8789
  async function bumpResourceGeneration2(root, resource, options) {
8129
8790
  const token = randomUUID3();
@@ -8141,7 +8802,7 @@ async function bumpResourceGeneration2(root, resource, options) {
8141
8802
  }
8142
8803
  async function readResourceGeneration2(root, resource) {
8143
8804
  try {
8144
- const token = await fs18.readFile(resourceGenerationPath2(root, resource), "utf-8");
8805
+ const token = await fs19.readFile(resourceGenerationPath2(root, resource), "utf-8");
8145
8806
  return { ok: true, token };
8146
8807
  } catch (err) {
8147
8808
  if (isNodeError(err) && err.code === "ENOENT")
@@ -8168,10 +8829,10 @@ var init_resource_generation = __esm({
8168
8829
  });
8169
8830
 
8170
8831
  // dist/content-index-registry.js
8171
- import path23 from "node:path";
8832
+ import path24 from "node:path";
8172
8833
  function invalidateContentIndexesForRoot2(root) {
8173
- const rootKey = path23.resolve(root);
8174
- const prefix = rootKey + path23.sep;
8834
+ const rootKey = path24.resolve(root);
8835
+ const prefix = rootKey + path24.sep;
8175
8836
  for (const [storeRoot, refs] of registry2) {
8176
8837
  if (storeRoot !== rootKey && !storeRoot.startsWith(prefix))
8177
8838
  continue;
@@ -8229,7 +8890,7 @@ var init_normalize3 = __esm({
8229
8890
  function isValidContentId2(id) {
8230
8891
  return CONTENT_ID_PATTERN2.test(id);
8231
8892
  }
8232
- function parseSlug(slug) {
8893
+ function parseSlug2(slug) {
8233
8894
  if (!slug) {
8234
8895
  return {
8235
8896
  ok: false,
@@ -8339,7 +9000,7 @@ var init_content_id_index = __esm({
8339
9000
  });
8340
9001
 
8341
9002
  // dist/schema/meta-loader.js
8342
- import { promises as fs19 } from "fs";
9003
+ import { promises as fs20 } from "fs";
8343
9004
  import { join as join4 } from "pathe";
8344
9005
  import { z as z8 } from "zod";
8345
9006
  import chokidar2 from "chokidar";
@@ -8349,7 +9010,7 @@ function stripEmbeddedIdFromName2(name) {
8349
9010
  async function scanForCollectionMeta2(baseDir, relativePath = "") {
8350
9011
  const collections = [];
8351
9012
  try {
8352
- const entries = await fs19.readdir(baseDir, { withFileTypes: true });
9013
+ const entries = await fs20.readdir(baseDir, { withFileTypes: true });
8353
9014
  for (const entry of entries) {
8354
9015
  if (!entry.isDirectory())
8355
9016
  continue;
@@ -8360,8 +9021,8 @@ async function scanForCollectionMeta2(baseDir, relativePath = "") {
8360
9021
  const absolutePath = join4(baseDir, folderName);
8361
9022
  const metaPath = join4(absolutePath, ".collection.json");
8362
9023
  try {
8363
- await fs19.access(metaPath);
8364
- const content = await fs19.readFile(metaPath, "utf-8");
9024
+ await fs20.access(metaPath);
9025
+ const content = await fs20.readFile(metaPath, "utf-8");
8365
9026
  const parsed = JSON.parse(content);
8366
9027
  const meta = collectionMetaSchema2.parse(parsed);
8367
9028
  collections.push({
@@ -8393,7 +9054,7 @@ async function loadCollectionMetaFiles2(contentRoot) {
8393
9054
  let root = null;
8394
9055
  const rootMetaPath = join4(contentRoot, ".collection.json");
8395
9056
  try {
8396
- await fs19.access(rootMetaPath);
9057
+ await fs20.access(rootMetaPath);
8397
9058
  } catch (err) {
8398
9059
  if (err.code === "ENOENT") {
8399
9060
  } else {
@@ -8401,7 +9062,7 @@ async function loadCollectionMetaFiles2(contentRoot) {
8401
9062
  }
8402
9063
  }
8403
9064
  try {
8404
- const content = await fs19.readFile(rootMetaPath, "utf-8");
9065
+ const content = await fs20.readFile(rootMetaPath, "utf-8");
8405
9066
  const parsed = JSON.parse(content);
8406
9067
  root = rootCollectionMetaSchema2.parse(parsed);
8407
9068
  } catch (err) {
@@ -8548,7 +9209,12 @@ var init_field3 = __esm({
8548
9209
  collections: z9.array(z9.string().min(1)).min(1).optional(),
8549
9210
  entryTypes: z9.array(z9.string().min(1)).min(1).optional(),
8550
9211
  displayField: z9.string().min(1).optional(),
8551
- options: z9.array(referenceOptionSchema3).optional()
9212
+ options: z9.array(referenceOptionSchema3).optional(),
9213
+ // Must be listed here, not just on ReferenceFieldConfig: zod strips unknown keys by
9214
+ // default, so a consumer that adopts this schema's parse output would silently delete a
9215
+ // runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
9216
+ // absent on purpose — it is type-inference-only and stripping it changes no behavior.)
9217
+ includeBody: z9.boolean().optional()
8552
9218
  });
8553
9219
  fieldHolder3 = [z9.never()];
8554
9220
  blockSchema3 = z9.object({
@@ -8658,7 +9324,7 @@ var init_media3 = __esm({
8658
9324
 
8659
9325
  // dist/config/schemas/config.js
8660
9326
  import { z as z12 } from "zod";
8661
- var defaultBranchAccessSchema3, defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3, defaultPathAccessSchema3, defaultBaseBranchSchema3, defaultRemoteNameSchema3, defaultRemoteUrlSchema3, gitBotAuthorNameSchema3, gitBotAuthorEmailSchema3, githubTokenEnvVarSchema3, operatingModeSchema3, deployedAsSchema3, contentRootSchema3, sourceRootSchema3, deploymentNameSchema3, devContentSyncSchema3, devConfigSchema3, editorConfigSchema3, CanopyConfigSchema3, DEFAULT_PROD_WORKSPACE3;
9327
+ var defaultBranchAccessSchema3, defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3, defaultPathAccessSchema3, defaultBaseBranchSchema3, defaultRemoteNameSchema3, defaultRemoteUrlSchema3, gitBotAuthorNameSchema3, gitBotAuthorEmailSchema3, githubTokenEnvVarSchema3, operatingModeSchema3, deployedAsSchema3, contentRootSchema3, sourceRootSchema3, basePathSchema3, deploymentNameSchema3, devContentSyncSchema3, devConfigSchema3, editorConfigSchema3, CanopyConfigSchema3, DEFAULT_PROD_WORKSPACE3;
8662
9328
  var init_config4 = __esm({
8663
9329
  "dist/config/schemas/config.js"() {
8664
9330
  "use strict";
@@ -8682,6 +9348,9 @@ var init_config4 = __esm({
8682
9348
  deployedAsSchema3 = z12.enum(["static", "server"]).default("server");
8683
9349
  contentRootSchema3 = relativePathSchema3.default("content");
8684
9350
  sourceRootSchema3 = z12.string().min(1).optional();
9351
+ basePathSchema3 = z12.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
9352
+ message: "basePath must not be empty or only whitespace"
9353
+ }).optional();
8685
9354
  deploymentNameSchema3 = z12.string().default("prod");
8686
9355
  devContentSyncSchema3 = z12.enum(["off", "warn"]).default("warn");
8687
9356
  devConfigSchema3 = z12.object({
@@ -8744,6 +9413,7 @@ var init_config4 = __esm({
8744
9413
  deploymentName: deploymentNameSchema3.optional(),
8745
9414
  contentRoot: contentRootSchema3.default("content"),
8746
9415
  sourceRoot: sourceRootSchema3.optional(),
9416
+ basePath: basePathSchema3,
8747
9417
  editor: editorConfigSchema3.optional(),
8748
9418
  authPlugin: z12.custom().optional(),
8749
9419
  entryLinkUrl: z12.custom().optional(),
@@ -8830,13 +9500,13 @@ var init_content_index_generation = __esm({
8830
9500
  });
8831
9501
 
8832
9502
  // dist/sync-core.js
8833
- import fs20 from "node:fs/promises";
8834
- import path24 from "node:path";
9503
+ import fs21 from "node:fs/promises";
9504
+ import path25 from "node:path";
8835
9505
  import { simpleGit as simpleGit4 } from "simple-git";
8836
9506
  function assertWithinDir(resolved, parent, label) {
8837
- const normalizedResolved = path24.resolve(resolved);
8838
- const normalizedParent = path24.resolve(parent);
8839
- if (!normalizedResolved.startsWith(normalizedParent + path24.sep) && normalizedResolved !== normalizedParent) {
9507
+ const normalizedResolved = path25.resolve(resolved);
9508
+ const normalizedParent = path25.resolve(parent);
9509
+ if (!normalizedResolved.startsWith(normalizedParent + path25.sep) && normalizedResolved !== normalizedParent) {
8840
9510
  throw new Error(`${label} escapes the expected directory: ${resolved}`);
8841
9511
  }
8842
9512
  }
@@ -8844,19 +9514,19 @@ async function safeReplaceDir(oldDir, newDir) {
8844
9514
  const backupDir = `${oldDir}.sync-backup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
8845
9515
  const oldExists = await filePathExists(oldDir);
8846
9516
  if (oldExists) {
8847
- await fs20.rename(oldDir, backupDir);
9517
+ await fs21.rename(oldDir, backupDir);
8848
9518
  }
8849
9519
  try {
8850
- await fs20.rename(newDir, oldDir);
9520
+ await fs21.rename(newDir, oldDir);
8851
9521
  } catch (err) {
8852
9522
  if (oldExists) {
8853
- await fs20.rename(backupDir, oldDir).catch(() => {
9523
+ await fs21.rename(backupDir, oldDir).catch(() => {
8854
9524
  });
8855
9525
  }
8856
9526
  throw err;
8857
9527
  }
8858
9528
  if (oldExists) {
8859
- await fs20.rm(backupDir, { recursive: true, force: true }).catch(() => {
9529
+ await fs21.rm(backupDir, { recursive: true, force: true }).catch(() => {
8860
9530
  });
8861
9531
  }
8862
9532
  }
@@ -8864,7 +9534,7 @@ async function listFilesRecursive(dir, prefix = "") {
8864
9534
  const results = [];
8865
9535
  let entries;
8866
9536
  try {
8867
- entries = await fs20.readdir(dir, { withFileTypes: true });
9537
+ entries = await fs21.readdir(dir, { withFileTypes: true });
8868
9538
  } catch {
8869
9539
  return results;
8870
9540
  }
@@ -8875,7 +9545,7 @@ async function listFilesRecursive(dir, prefix = "") {
8875
9545
  continue;
8876
9546
  const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
8877
9547
  if (entry.isDirectory()) {
8878
- results.push(...await listFilesRecursive(path24.join(dir, entry.name), rel));
9548
+ results.push(...await listFilesRecursive(path25.join(dir, entry.name), rel));
8879
9549
  } else {
8880
9550
  results.push(rel);
8881
9551
  }
@@ -8883,19 +9553,19 @@ async function listFilesRecursive(dir, prefix = "") {
8883
9553
  return results;
8884
9554
  }
8885
9555
  async function copyDir(src, dest) {
8886
- await fs20.mkdir(dest, { recursive: true });
8887
- const entries = await fs20.readdir(src, { withFileTypes: true });
9556
+ await fs21.mkdir(dest, { recursive: true });
9557
+ const entries = await fs21.readdir(src, { withFileTypes: true });
8888
9558
  for (const entry of entries) {
8889
9559
  if (entry.name === ".git")
8890
9560
  continue;
8891
9561
  if (entry.isSymbolicLink())
8892
9562
  continue;
8893
- const srcPath = path24.join(src, entry.name);
8894
- const destPath = path24.join(dest, entry.name);
9563
+ const srcPath = path25.join(src, entry.name);
9564
+ const destPath = path25.join(dest, entry.name);
8895
9565
  if (entry.isDirectory()) {
8896
9566
  await copyDir(srcPath, destPath);
8897
9567
  } else {
8898
- await fs20.copyFile(srcPath, destPath);
9568
+ await fs21.copyFile(srcPath, destPath);
8899
9569
  }
8900
9570
  }
8901
9571
  }
@@ -8904,14 +9574,14 @@ async function pushContentToWorkspace(options) {
8904
9574
  if (!await filePathExists(srcContentDir)) {
8905
9575
  return { fileCount: 0 };
8906
9576
  }
8907
- const wsContentDir = path24.join(branchPath, contentRoot);
9577
+ const wsContentDir = path25.join(branchPath, contentRoot);
8908
9578
  assertWithinDir(wsContentDir, branchPath, "content-root");
8909
9579
  const tmpDir = `${wsContentDir}.sync-tmp-${Date.now()}`;
8910
9580
  try {
8911
9581
  await copyDir(srcContentDir, tmpDir);
8912
9582
  await safeReplaceDir(wsContentDir, tmpDir);
8913
9583
  } catch (err) {
8914
- await fs20.rm(tmpDir, { recursive: true, force: true }).catch(() => {
9584
+ await fs21.rm(tmpDir, { recursive: true, force: true }).catch(() => {
8915
9585
  });
8916
9586
  throw err;
8917
9587
  }
@@ -8955,19 +9625,21 @@ import lockfile3 from "proper-lockfile";
8955
9625
  var init_provisioning_lock = __esm({
8956
9626
  "dist/utils/provisioning-lock.js"() {
8957
9627
  "use strict";
9628
+ init_error();
9629
+ init_logger3();
8958
9630
  }
8959
9631
  });
8960
9632
 
8961
9633
  // dist/git-manager.js
8962
- import fs21 from "node:fs/promises";
8963
- import path25 from "node:path";
9634
+ import fs24 from "node:fs/promises";
9635
+ import path26 from "node:path";
8964
9636
  import { simpleGit as simpleGit6 } from "simple-git";
8965
9637
  async function ensureGitExcludePattern2(repoPath, pattern) {
8966
- const excludePath = path25.join(repoPath, ".git", "info", "exclude");
8967
- await fs21.mkdir(path25.dirname(excludePath), { recursive: true });
9638
+ const excludePath = path26.join(repoPath, ".git", "info", "exclude");
9639
+ await fs24.mkdir(path26.dirname(excludePath), { recursive: true });
8968
9640
  let content = "";
8969
9641
  try {
8970
- content = await fs21.readFile(excludePath, "utf-8");
9642
+ content = await fs24.readFile(excludePath, "utf-8");
8971
9643
  } catch (err) {
8972
9644
  if (!isNotFoundError(err))
8973
9645
  throw err;
@@ -8979,7 +9651,7 @@ async function ensureGitExcludePattern2(repoPath, pattern) {
8979
9651
  }
8980
9652
  const needsLeadingNewline = content.length > 0 && !content.endsWith("\n");
8981
9653
  const newContent = content + (needsLeadingNewline ? "\n" : "") + pattern + "\n";
8982
- await fs21.writeFile(excludePath, newContent, "utf-8");
9654
+ await fs24.writeFile(excludePath, newContent, "utf-8");
8983
9655
  log9.debug("git", "Added pattern to .git/info/exclude", { pattern });
8984
9656
  }
8985
9657
  var log9;
@@ -9136,7 +9808,7 @@ var init_config5 = __esm({
9136
9808
  });
9137
9809
 
9138
9810
  // dist/operating-mode/client-unsafe-strategy.js
9139
- import path26 from "node:path";
9811
+ import path27 from "node:path";
9140
9812
  function operatingStrategy3(mode) {
9141
9813
  const cached = strategyCache3.get(mode);
9142
9814
  if (cached)
@@ -9171,32 +9843,32 @@ var init_client_unsafe_strategy2 = __esm({
9171
9843
  // - shouldCommit(), shouldPush()
9172
9844
  // Add client-unsafe methods (use Node.js APIs)
9173
9845
  getWorkspaceRoot(_sourceRoot) {
9174
- return path26.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE3);
9846
+ return path27.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE3);
9175
9847
  }
9176
9848
  getContentRoot(contentRoot, sourceRoot) {
9177
- return path26.resolve(sourceRoot ?? process.cwd(), contentRoot);
9849
+ return path27.resolve(sourceRoot ?? process.cwd(), contentRoot);
9178
9850
  }
9179
9851
  getContentBranchesRoot(sourceRoot) {
9180
- return path26.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
9852
+ return path27.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
9181
9853
  }
9182
9854
  getContentBranchRoot(branchName, sourceRoot) {
9183
- return path26.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
9855
+ return path27.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
9184
9856
  }
9185
9857
  getGitExcludePattern() {
9186
9858
  return ".canopy-meta/";
9187
9859
  }
9188
9860
  getPermissionsFilePath(root) {
9189
- return path26.join(root, this.getPermissionsFileName());
9861
+ return path27.join(root, this.getPermissionsFileName());
9190
9862
  }
9191
9863
  getGroupsFilePath(root) {
9192
- return path26.join(root, this.getGroupsFileName());
9864
+ return path27.join(root, this.getGroupsFileName());
9193
9865
  }
9194
9866
  getRemoteUrlConfig() {
9195
9867
  return {
9196
9868
  shouldAutoInitLocal: false,
9197
9869
  defaultRemotePath: "",
9198
9870
  envVarName: "CANOPYCMS_REMOTE_URL",
9199
- autoDetectRemotePath: path26.join(this.getWorkspaceRoot(), "remote.git")
9871
+ autoDetectRemotePath: path27.join(this.getWorkspaceRoot(), "remote.git")
9200
9872
  };
9201
9873
  }
9202
9874
  requiresExistingRepo() {
@@ -9208,7 +9880,7 @@ var init_client_unsafe_strategy2 = __esm({
9208
9880
  return `canopycms-settings-${resolveDeploymentName3(config, "prod")}`;
9209
9881
  }
9210
9882
  getSettingsRoot(sourceRoot) {
9211
- return path26.join(this.getWorkspaceRoot(sourceRoot), "settings");
9883
+ return path27.join(this.getWorkspaceRoot(sourceRoot), "settings");
9212
9884
  }
9213
9885
  usesSeparateSettingsBranch() {
9214
9886
  return true;
@@ -9225,25 +9897,25 @@ var init_client_unsafe_strategy2 = __esm({
9225
9897
  DevStrategy3 = class extends DevClientSafeStrategy3 {
9226
9898
  // Inherits client-safe methods from DevClientSafeStrategy
9227
9899
  getWorkspaceRoot(sourceRoot) {
9228
- return path26.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
9900
+ return path27.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
9229
9901
  }
9230
9902
  getContentRoot(contentRoot, sourceRoot) {
9231
- return path26.resolve(sourceRoot ?? process.cwd(), contentRoot);
9903
+ return path27.resolve(sourceRoot ?? process.cwd(), contentRoot);
9232
9904
  }
9233
9905
  getContentBranchesRoot(sourceRoot) {
9234
- return path26.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
9906
+ return path27.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
9235
9907
  }
9236
9908
  getContentBranchRoot(branchName, sourceRoot) {
9237
- return path26.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
9909
+ return path27.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
9238
9910
  }
9239
9911
  getGitExcludePattern() {
9240
9912
  return ".canopy-meta/";
9241
9913
  }
9242
9914
  getPermissionsFilePath(root) {
9243
- return path26.join(root, this.getPermissionsFileName());
9915
+ return path27.join(root, this.getPermissionsFileName());
9244
9916
  }
9245
9917
  getGroupsFilePath(root) {
9246
- return path26.join(root, this.getGroupsFileName());
9918
+ return path27.join(root, this.getGroupsFileName());
9247
9919
  }
9248
9920
  getRemoteUrlConfig() {
9249
9921
  return {
@@ -9261,7 +9933,7 @@ var init_client_unsafe_strategy2 = __esm({
9261
9933
  return `canopycms-settings-${resolveDeploymentName3(config, "local")}`;
9262
9934
  }
9263
9935
  getSettingsRoot(sourceRoot) {
9264
- return path26.join(this.getWorkspaceRoot(sourceRoot), "settings");
9936
+ return path27.join(this.getWorkspaceRoot(sourceRoot), "settings");
9265
9937
  }
9266
9938
  usesSeparateSettingsBranch() {
9267
9939
  return true;
@@ -9293,8 +9965,8 @@ __export(sync_exports, {
9293
9965
  SyncError: () => SyncError,
9294
9966
  sync: () => sync
9295
9967
  });
9296
- import fs24 from "node:fs/promises";
9297
- import path27 from "node:path";
9968
+ import fs25 from "node:fs/promises";
9969
+ import path28 from "node:path";
9298
9970
  import { simpleGit as simpleGit7 } from "simple-git";
9299
9971
  import * as p2 from "@clack/prompts";
9300
9972
  async function detectCurrentBranch(projectDir) {
@@ -9307,7 +9979,7 @@ async function detectCurrentBranch(projectDir) {
9307
9979
  async function selectBranch(options, branchesDir) {
9308
9980
  let branches = [];
9309
9981
  if (await filePathExists(branchesDir)) {
9310
- const entries = await fs24.readdir(branchesDir, { withFileTypes: true });
9982
+ const entries = await fs25.readdir(branchesDir, { withFileTypes: true });
9311
9983
  branches = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
9312
9984
  }
9313
9985
  let branchName = options.branch;
@@ -9335,11 +10007,11 @@ async function selectBranch(options, branchesDir) {
9335
10007
  branchName = result;
9336
10008
  }
9337
10009
  }
9338
- const branchPath = path27.join(branchesDir, branchName);
10010
+ const branchPath = path28.join(branchesDir, branchName);
9339
10011
  assertWithinDir(branchPath, branchesDir, "--branch");
9340
10012
  if (!branches.includes(branchName)) {
9341
10013
  if (options.autoCreate) {
9342
- await fs24.mkdir(branchPath, { recursive: true });
10014
+ await fs25.mkdir(branchPath, { recursive: true });
9343
10015
  const wsGit = simpleGit7({ baseDir: branchPath });
9344
10016
  await wsGit.init();
9345
10017
  await wsGit.checkoutLocalBranch(branchName);
@@ -9356,8 +10028,8 @@ async function selectBranch(options, branchesDir) {
9356
10028
  async function syncPush(options) {
9357
10029
  const { projectDir } = options;
9358
10030
  const contentRoot = options.contentRoot || "content";
9359
- const branchesDir = path27.join(projectDir, ".canopy-dev", "content-branches");
9360
- const srcContentDir = path27.join(projectDir, contentRoot);
10031
+ const branchesDir = path28.join(projectDir, ".canopy-dev", "content-branches");
10032
+ const srcContentDir = path28.join(projectDir, contentRoot);
9361
10033
  assertWithinDir(srcContentDir, projectDir, "--content-root");
9362
10034
  if (!await filePathExists(srcContentDir)) {
9363
10035
  throw new SyncError(`Content directory not found: ${contentRoot}/ (expected at ${srcContentDir})`);
@@ -9365,7 +10037,7 @@ async function syncPush(options) {
9365
10037
  const branchName = await selectBranch({ ...options, projectDir, autoCreate: true }, branchesDir);
9366
10038
  if (!branchName)
9367
10039
  return { fileCount: 0 };
9368
- const branchPath = path27.join(branchesDir, branchName);
10040
+ const branchPath = path28.join(branchesDir, branchName);
9369
10041
  assertWithinDir(branchPath, branchesDir, "--branch");
9370
10042
  const wsGit = simpleGit7({ baseDir: branchPath });
9371
10043
  const status = await wsGit.status();
@@ -9423,19 +10095,19 @@ async function syncPush(options) {
9423
10095
  async function syncPull(options) {
9424
10096
  const { projectDir } = options;
9425
10097
  const contentRoot = options.contentRoot || "content";
9426
- const branchesDir = path27.join(projectDir, ".canopy-dev", "content-branches");
10098
+ const branchesDir = path28.join(projectDir, ".canopy-dev", "content-branches");
9427
10099
  const branchName = await selectBranch({ ...options, projectDir }, branchesDir);
9428
10100
  if (!branchName)
9429
10101
  return { fileCount: 0 };
9430
- const branchPath = path27.join(branchesDir, branchName);
10102
+ const branchPath = path28.join(branchesDir, branchName);
9431
10103
  assertWithinDir(branchPath, branchesDir, "--branch");
9432
- const branchContentDir = path27.join(branchPath, contentRoot);
10104
+ const branchContentDir = path28.join(branchPath, contentRoot);
9433
10105
  assertWithinDir(branchContentDir, branchPath, "--content-root");
9434
10106
  if (!await filePathExists(branchContentDir)) {
9435
10107
  throw new SyncError(`Content directory not found in branch workspace: ${branchName}/${contentRoot}`);
9436
10108
  }
9437
10109
  p2.log.step(`Pulling content from branch: ${branchName}`);
9438
- const destContentDir = path27.join(projectDir, contentRoot);
10110
+ const destContentDir = path28.join(projectDir, contentRoot);
9439
10111
  assertWithinDir(destContentDir, projectDir, "--content-root");
9440
10112
  const sourceGit = simpleGit7({ baseDir: projectDir });
9441
10113
  const status = await sourceGit.status();
@@ -9487,7 +10159,7 @@ async function syncPull(options) {
9487
10159
  await copyDir(branchContentDir, tmpDestDir);
9488
10160
  await safeReplaceDir(destContentDir, tmpDestDir);
9489
10161
  } catch (err) {
9490
- await fs24.rm(tmpDestDir, { recursive: true, force: true }).catch(() => {
10162
+ await fs25.rm(tmpDestDir, { recursive: true, force: true }).catch(() => {
9491
10163
  });
9492
10164
  throw err;
9493
10165
  }
@@ -9511,8 +10183,8 @@ async function syncPull(options) {
9511
10183
  async function syncBoth(options) {
9512
10184
  const { projectDir } = options;
9513
10185
  const contentRoot = options.contentRoot || "content";
9514
- const branchesDir = path27.join(projectDir, ".canopy-dev", "content-branches");
9515
- const srcContentDir = path27.join(projectDir, contentRoot);
10186
+ const branchesDir = path28.join(projectDir, ".canopy-dev", "content-branches");
10187
+ const srcContentDir = path28.join(projectDir, contentRoot);
9516
10188
  assertWithinDir(srcContentDir, projectDir, "--content-root");
9517
10189
  if (!await filePathExists(srcContentDir)) {
9518
10190
  throw new SyncError(`Content directory not found: ${contentRoot}/ (expected at ${srcContentDir})`);
@@ -9520,7 +10192,7 @@ async function syncBoth(options) {
9520
10192
  const branchName = await selectBranch({ ...options, projectDir, autoCreate: true }, branchesDir);
9521
10193
  if (!branchName)
9522
10194
  return { pushed: 0, pulled: 0 };
9523
- const branchPath = path27.join(branchesDir, branchName);
10195
+ const branchPath = path28.join(branchesDir, branchName);
9524
10196
  assertWithinDir(branchPath, branchesDir, "--branch");
9525
10197
  const wsGit = simpleGit7({ baseDir: branchPath });
9526
10198
  const status = await wsGit.status();
@@ -9555,14 +10227,14 @@ async function syncBoth(options) {
9555
10227
  try {
9556
10228
  const incomingBranch = `sync-incoming-${Date.now()}`;
9557
10229
  await wsGit.raw(["checkout", "-b", incomingBranch, baseRef]);
9558
- const wsContentDir = path27.join(branchPath, contentRoot);
10230
+ const wsContentDir = path28.join(branchPath, contentRoot);
9559
10231
  assertWithinDir(wsContentDir, branchPath, "--content-root");
9560
10232
  const tmpDir = `${wsContentDir}.sync-tmp-${Date.now()}`;
9561
10233
  try {
9562
10234
  await copyDir(srcContentDir, tmpDir);
9563
10235
  await safeReplaceDir(wsContentDir, tmpDir);
9564
10236
  } catch (err) {
9565
- await fs24.rm(tmpDir, { recursive: true, force: true }).catch(() => {
10237
+ await fs25.rm(tmpDir, { recursive: true, force: true }).catch(() => {
9566
10238
  });
9567
10239
  await wsGit.checkout(currentBranch);
9568
10240
  await wsGit.raw(["branch", "-D", incomingBranch]).catch(() => {
@@ -9619,11 +10291,11 @@ async function syncBoth(options) {
9619
10291
  }
9620
10292
  async function syncAbort(options) {
9621
10293
  const { projectDir } = options;
9622
- const branchesDir = path27.join(projectDir, ".canopy-dev", "content-branches");
10294
+ const branchesDir = path28.join(projectDir, ".canopy-dev", "content-branches");
9623
10295
  const branchName = await selectBranch({ ...options, projectDir }, branchesDir);
9624
10296
  if (!branchName)
9625
10297
  return;
9626
- const branchPath = path27.join(branchesDir, branchName);
10298
+ const branchPath = path28.join(branchesDir, branchName);
9627
10299
  assertWithinDir(branchPath, branchesDir, "--branch");
9628
10300
  const wsGit = simpleGit7({ baseDir: branchPath });
9629
10301
  const status = await wsGit.status();
@@ -9751,8 +10423,8 @@ var init_async_mutex = __esm({
9751
10423
  });
9752
10424
 
9753
10425
  // dist/utils/occ-json-write.js
9754
- import fs25 from "node:fs/promises";
9755
- import path28 from "node:path";
10426
+ import fs26 from "node:fs/promises";
10427
+ import path29 from "node:path";
9756
10428
  import { randomUUID as randomUUID4 } from "node:crypto";
9757
10429
  import lockfile4 from "proper-lockfile";
9758
10430
  async function writeOccJsonFile2(filePath, payload, options) {
@@ -9761,29 +10433,29 @@ async function writeOccJsonFile2(filePath, payload, options) {
9761
10433
  const writeId = randomUUID4();
9762
10434
  const data = { ...payload, version: newVersion, writeId };
9763
10435
  const content = JSON.stringify(data, null, 2) + (trailingNewline ? "\n" : "");
9764
- const dir = path28.dirname(filePath);
9765
- await fs25.mkdir(dir, { recursive: true });
10436
+ const dir = path29.dirname(filePath);
10437
+ await fs26.mkdir(dir, { recursive: true });
9766
10438
  const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
9767
- await fs25.writeFile(tempPath, content, "utf-8");
10439
+ await fs26.writeFile(tempPath, content, "utf-8");
9768
10440
  if (expectedVersion === null) {
9769
10441
  try {
9770
- await fs25.link(tempPath, filePath);
10442
+ await fs26.link(tempPath, filePath);
9771
10443
  } catch (err) {
9772
- await fs25.unlink(tempPath).catch(() => {
10444
+ await fs26.unlink(tempPath).catch(() => {
9773
10445
  });
9774
10446
  if (isFileExistsError(err)) {
9775
10447
  throw new OccWriteConflictError2();
9776
10448
  }
9777
10449
  throw err;
9778
10450
  }
9779
- await fs25.unlink(tempPath).catch(() => {
10451
+ await fs26.unlink(tempPath).catch(() => {
9780
10452
  });
9781
10453
  return { version: newVersion, writeId };
9782
10454
  }
9783
10455
  try {
9784
10456
  let currentVersion = null;
9785
10457
  try {
9786
- const current = JSON.parse(await fs25.readFile(filePath, "utf-8"));
10458
+ const current = JSON.parse(await fs26.readFile(filePath, "utf-8"));
9787
10459
  currentVersion = current.version ?? 0;
9788
10460
  } catch {
9789
10461
  currentVersion = null;
@@ -9791,16 +10463,16 @@ async function writeOccJsonFile2(filePath, payload, options) {
9791
10463
  if (currentVersion !== expectedVersion) {
9792
10464
  throw new OccWriteConflictError2();
9793
10465
  }
9794
- await fs25.rename(tempPath, filePath);
10466
+ await fs26.rename(tempPath, filePath);
9795
10467
  if (settleMs > 0) {
9796
10468
  await new Promise((resolve2) => setTimeout(resolve2, settleMs));
9797
10469
  }
9798
- const afterWrite = JSON.parse(await fs25.readFile(filePath, "utf-8"));
10470
+ const afterWrite = JSON.parse(await fs26.readFile(filePath, "utf-8"));
9799
10471
  if (afterWrite.writeId !== writeId) {
9800
10472
  throw new OccWriteConflictError2();
9801
10473
  }
9802
10474
  } catch (err) {
9803
- await fs25.unlink(tempPath).catch(() => {
10475
+ await fs26.unlink(tempPath).catch(() => {
9804
10476
  });
9805
10477
  throw err;
9806
10478
  }
@@ -9826,8 +10498,8 @@ async function withOccRetry2(operation, options) {
9826
10498
  throw new Error("Unreachable");
9827
10499
  }
9828
10500
  async function withOccFileLock2(filePath, fn) {
9829
- const dir = path28.dirname(filePath);
9830
- await fs25.mkdir(dir, { recursive: true });
10501
+ const dir = path29.dirname(filePath);
10502
+ await fs26.mkdir(dir, { recursive: true });
9831
10503
  const MAX_ATTEMPTS = 14;
9832
10504
  let release = null;
9833
10505
  for (let attempt = 1; release === null; attempt++) {
@@ -9886,8 +10558,8 @@ var init_occ_json_write = __esm({
9886
10558
  });
9887
10559
 
9888
10560
  // dist/branch-registry.js
9889
- import fs26 from "node:fs/promises";
9890
- import path29 from "node:path";
10561
+ import fs27 from "node:fs/promises";
10562
+ import path30 from "node:path";
9891
10563
  var log12, REGISTRY_FILE2, REGISTRY_STALE_FILE2, REGISTRY_TEMP_FILE2, REGISTRY_VERSION2, RESOURCE4, GET_MISS_REFRESH_MIN_INTERVAL_MS2, BranchRegistry2;
9892
10564
  var init_branch_registry = __esm({
9893
10565
  "dist/branch-registry.js"() {
@@ -9908,10 +10580,10 @@ var init_branch_registry = __esm({
9908
10580
  constructor(root) {
9909
10581
  this.regenInFlight = null;
9910
10582
  this.lastForcedRefreshMs = 0;
9911
- this.root = path29.resolve(root);
9912
- this.registryPath = path29.join(this.root, REGISTRY_FILE2);
9913
- this.stalePath = path29.join(this.root, REGISTRY_STALE_FILE2);
9914
- this.tempPath = path29.join(this.root, REGISTRY_TEMP_FILE2);
10583
+ this.root = path30.resolve(root);
10584
+ this.registryPath = path30.join(this.root, REGISTRY_FILE2);
10585
+ this.stalePath = path30.join(this.root, REGISTRY_STALE_FILE2);
10586
+ this.tempPath = path30.join(this.root, REGISTRY_TEMP_FILE2);
9915
10587
  }
9916
10588
  /**
9917
10589
  * Returns all branches. Uses the cached snapshot if its embedded generation
@@ -9920,7 +10592,7 @@ var init_branch_registry = __esm({
9920
10592
  async list() {
9921
10593
  let parsed;
9922
10594
  try {
9923
- const raw = await fs26.readFile(this.registryPath, "utf8");
10595
+ const raw = await fs27.readFile(this.registryPath, "utf8");
9924
10596
  parsed = JSON.parse(raw);
9925
10597
  } catch (err) {
9926
10598
  if (isNotFoundError(err)) {
@@ -10009,7 +10681,7 @@ var init_branch_registry = __esm({
10009
10681
  async performRegenerate() {
10010
10682
  const read = await readResourceGeneration2(this.root, RESOURCE4);
10011
10683
  const branches = await this.scanBranchDirectories();
10012
- await fs26.unlink(this.stalePath).catch(() => {
10684
+ await fs27.unlink(this.stalePath).catch(() => {
10013
10685
  });
10014
10686
  if (!read.ok) {
10015
10687
  return branches;
@@ -10020,12 +10692,12 @@ var init_branch_registry = __esm({
10020
10692
  generation: read.token
10021
10693
  };
10022
10694
  const uniqueTempPath = `${this.tempPath}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
10023
- await fs26.mkdir(this.root, { recursive: true });
10024
- await fs26.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
10695
+ await fs27.mkdir(this.root, { recursive: true });
10696
+ await fs27.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
10025
10697
  try {
10026
- await fs26.rename(uniqueTempPath, this.registryPath);
10698
+ await fs27.rename(uniqueTempPath, this.registryPath);
10027
10699
  } catch (err) {
10028
- await fs26.unlink(uniqueTempPath).catch(() => {
10700
+ await fs27.unlink(uniqueTempPath).catch(() => {
10029
10701
  });
10030
10702
  throw err;
10031
10703
  }
@@ -10039,12 +10711,12 @@ var init_branch_registry = __esm({
10039
10711
  async scanBranchDirectories() {
10040
10712
  const branches = [];
10041
10713
  try {
10042
- const entries = await fs26.readdir(this.root, { withFileTypes: true });
10714
+ const entries = await fs27.readdir(this.root, { withFileTypes: true });
10043
10715
  for (const entry of entries) {
10044
10716
  if (!entry.isDirectory() || entry.name.startsWith(".")) {
10045
10717
  continue;
10046
10718
  }
10047
- const branchRoot = path29.join(this.root, entry.name);
10719
+ const branchRoot = path30.join(this.root, entry.name);
10048
10720
  let meta;
10049
10721
  try {
10050
10722
  meta = await BranchMetadataFileManager2.loadOnly(branchRoot);
@@ -10073,8 +10745,8 @@ var init_branch_registry = __esm({
10073
10745
  });
10074
10746
 
10075
10747
  // dist/branch-metadata.js
10076
- import fs27 from "node:fs/promises";
10077
- import path30 from "node:path";
10748
+ import fs28 from "node:fs/promises";
10749
+ import path31 from "node:path";
10078
10750
  var BRANCH_META_DIR2, BRANCH_META_FILE2, CURRENT_SCHEMA_VERSION2, BranchMetadataConflictError2, BranchMetadataCorruptError2, BranchMetadataFileManager2;
10079
10751
  var init_branch_metadata = __esm({
10080
10752
  "dist/branch-metadata.js"() {
@@ -10103,8 +10775,8 @@ var init_branch_metadata = __esm({
10103
10775
  };
10104
10776
  BranchMetadataFileManager2 = class _BranchMetadataFileManager2 {
10105
10777
  constructor(branchRoot, baseRoot, options) {
10106
- this.branchRoot = path30.resolve(branchRoot);
10107
- this.filePath = path30.join(this.branchRoot, BRANCH_META_DIR2, BRANCH_META_FILE2);
10778
+ this.branchRoot = path31.resolve(branchRoot);
10779
+ this.filePath = path31.join(this.branchRoot, BRANCH_META_DIR2, BRANCH_META_FILE2);
10108
10780
  this.baseRoot = baseRoot;
10109
10781
  this.settleMs = options?.settleMs;
10110
10782
  }
@@ -10113,10 +10785,10 @@ var init_branch_metadata = __esm({
10113
10785
  * Use this for read-only access (e.g., in registry scanning or loadBranchContext).
10114
10786
  */
10115
10787
  static async loadOnly(branchRoot) {
10116
- const resolvedRoot = path30.resolve(branchRoot);
10117
- const filePath = path30.join(resolvedRoot, BRANCH_META_DIR2, BRANCH_META_FILE2);
10788
+ const resolvedRoot = path31.resolve(branchRoot);
10789
+ const filePath = path31.join(resolvedRoot, BRANCH_META_DIR2, BRANCH_META_FILE2);
10118
10790
  try {
10119
- const raw = await fs27.readFile(filePath, "utf8");
10791
+ const raw = await fs28.readFile(filePath, "utf8");
10120
10792
  return JSON.parse(raw);
10121
10793
  } catch (err) {
10122
10794
  if (isNotFoundError(err)) {
@@ -10137,7 +10809,7 @@ var init_branch_metadata = __esm({
10137
10809
  }
10138
10810
  async load() {
10139
10811
  try {
10140
- const raw = await fs27.readFile(this.filePath, "utf8");
10812
+ const raw = await fs28.readFile(this.filePath, "utf8");
10141
10813
  const parsed = JSON.parse(raw);
10142
10814
  const version = parsed.version ?? 0;
10143
10815
  return { meta: parsed, version };
@@ -10199,7 +10871,7 @@ var init_branch_metadata = __esm({
10199
10871
  */
10200
10872
  async save(incoming) {
10201
10873
  try {
10202
- await fs27.stat(this.branchRoot);
10874
+ await fs28.stat(this.branchRoot);
10203
10875
  } catch (err) {
10204
10876
  if (isNotFoundError(err)) {
10205
10877
  throw new BranchMetadataConflictError2("Branch no longer exists");
@@ -10274,8 +10946,8 @@ __export(migrate_exports, {
10274
10946
  migrate: () => migrate,
10275
10947
  slugifyName: () => slugifyName
10276
10948
  });
10277
- import fs28 from "node:fs/promises";
10278
- import path31 from "node:path";
10949
+ import fs29 from "node:fs/promises";
10950
+ import path33 from "node:path";
10279
10951
  import * as p3 from "@clack/prompts";
10280
10952
  function slugifyName(name) {
10281
10953
  const slug = name.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
@@ -10283,7 +10955,7 @@ function slugifyName(name) {
10283
10955
  }
10284
10956
  function toValidSlug(name) {
10285
10957
  const slug = slugifyName(name);
10286
- const parsed = parseSlug(slug);
10958
+ const parsed = parseSlug2(slug);
10287
10959
  if (!parsed.ok) {
10288
10960
  throw new MigrateError(`Cannot derive a valid slug from "${name}" (got "${slug}"): ${parsed.error}`);
10289
10961
  }
@@ -10300,7 +10972,7 @@ function stripDirIdSuffix(dirName) {
10300
10972
  return dirName;
10301
10973
  }
10302
10974
  async function planDirectory(dirPath, isRoot, ctx) {
10303
- const entries = await fs28.readdir(dirPath, { withFileTypes: true });
10975
+ const entries = await fs29.readdir(dirPath, { withFileTypes: true });
10304
10976
  entries.sort((a, b) => a.name.localeCompare(b.name));
10305
10977
  const ops = [];
10306
10978
  let hasOwnFiles = false;
@@ -10309,7 +10981,7 @@ async function planDirectory(dirPath, isRoot, ctx) {
10309
10981
  if (entry.name.startsWith("."))
10310
10982
  continue;
10311
10983
  if (entry.isDirectory()) {
10312
- const subPath = path31.join(dirPath, entry.name);
10984
+ const subPath = path33.join(dirPath, entry.name);
10313
10985
  const sub = await planDirectory(subPath, false, ctx);
10314
10986
  if (!sub.hasContent)
10315
10987
  continue;
@@ -10317,10 +10989,10 @@ async function planDirectory(dirPath, isRoot, ctx) {
10317
10989
  ops.push(...sub.ops);
10318
10990
  if (!extractIdFromFilename2(entry.name)) {
10319
10991
  const newName = `${toValidSlug(entry.name)}.${generateId2()}`;
10320
- ops.push({ kind: "rename", from: subPath, to: path31.join(dirPath, newName) });
10992
+ ops.push({ kind: "rename", from: subPath, to: path33.join(dirPath, newName) });
10321
10993
  }
10322
10994
  } else if (entry.isFile()) {
10323
- const ext = path31.extname(entry.name).slice(1);
10995
+ const ext = path33.extname(entry.name).slice(1);
10324
10996
  const matchesFormat = ext === ctx.format || ctx.format === "yaml" && ext === "yml";
10325
10997
  if (!matchesFormat)
10326
10998
  continue;
@@ -10331,17 +11003,17 @@ async function planDirectory(dirPath, isRoot, ctx) {
10331
11003
  const newName = `${ctx.entryType}.${toValidSlug(base)}.${generateId2()}.${ext}`;
10332
11004
  ops.push({
10333
11005
  kind: "rename",
10334
- from: path31.join(dirPath, entry.name),
10335
- to: path31.join(dirPath, newName)
11006
+ from: path33.join(dirPath, entry.name),
11007
+ to: path33.join(dirPath, newName)
10336
11008
  });
10337
11009
  }
10338
11010
  }
10339
11011
  }
10340
11012
  const needsMeta = isRoot ? hasOwnFiles : hasContent;
10341
- const metaPath = path31.join(dirPath, ".collection.json");
11013
+ const metaPath = path33.join(dirPath, ".collection.json");
10342
11014
  if (needsMeta && !await filePathExists(metaPath)) {
10343
11015
  const entryTypeMeta = { name: ctx.entryType, format: ctx.format, schema: ctx.schema };
10344
- const meta = isRoot ? { entries: [entryTypeMeta] } : { name: toValidSlug(stripDirIdSuffix(path31.basename(dirPath))), entries: [entryTypeMeta] };
11016
+ const meta = isRoot ? { entries: [entryTypeMeta] } : { name: toValidSlug(stripDirIdSuffix(path33.basename(dirPath))), entries: [entryTypeMeta] };
10345
11017
  ops.push({ kind: "write", filePath: metaPath, content: JSON.stringify(meta, null, 2) + "\n" });
10346
11018
  }
10347
11019
  return { ops, hasContent };
@@ -10350,7 +11022,7 @@ async function migrate(options) {
10350
11022
  const { projectDir } = options;
10351
11023
  const contentRoot = options.contentRoot || "content";
10352
11024
  p3.intro("CanopyCMS migrate");
10353
- const contentDir = path31.join(projectDir, contentRoot);
11025
+ const contentDir = path33.join(projectDir, contentRoot);
10354
11026
  if (!await filePathExists(contentDir)) {
10355
11027
  throw new MigrateError(`Content directory not found: ${contentRoot}/ (expected at ${contentDir})`);
10356
11028
  }
@@ -10427,14 +11099,14 @@ async function migrate(options) {
10427
11099
  const applyOps = async () => {
10428
11100
  for (const op of ops) {
10429
11101
  if (op.kind === "rename") {
10430
- await fs28.rename(op.from, op.to);
11102
+ await fs29.rename(op.from, op.to);
10431
11103
  } else {
10432
- await fs28.writeFile(op.filePath, op.content, "utf-8");
11104
+ await fs29.writeFile(op.filePath, op.content, "utf-8");
10433
11105
  }
10434
11106
  }
10435
11107
  };
10436
11108
  if (isBranchClone) {
10437
- const lockPath = path31.join(projectDir, ".canopy-meta", "schema");
11109
+ const lockPath = path33.join(projectDir, ".canopy-meta", "schema");
10438
11110
  try {
10439
11111
  await withLock2(lockPath, () => withOccFileLock2(lockPath, applyOps));
10440
11112
  } catch (err) {
@@ -10482,7 +11154,7 @@ var init_migrate = __esm({
10482
11154
  this.name = "MigrateError";
10483
11155
  }
10484
11156
  };
10485
- describeOp = (op, baseDir) => op.kind === "rename" ? `rename ${path31.relative(baseDir, op.from)} \u2192 ${path31.basename(op.to)}` : `create ${path31.relative(baseDir, op.filePath)}`;
11157
+ describeOp = (op, baseDir) => op.kind === "rename" ? `rename ${path33.relative(baseDir, op.from)} \u2192 ${path33.basename(op.to)}` : `create ${path33.relative(baseDir, op.filePath)}`;
10486
11158
  }
10487
11159
  });
10488
11160
 
@@ -10551,6 +11223,10 @@ async function requireProjectRoot(command) {
10551
11223
  }
10552
11224
  return root;
10553
11225
  }
11226
+ var KNOWN_AUTH_MODES = ["clerk", "dev"];
11227
+ function isKnownAuthMode(value) {
11228
+ return KNOWN_AUTH_MODES.includes(value);
11229
+ }
10554
11230
  function resolveSyncSubcommand(sub) {
10555
11231
  if (sub && SYNC_SUBCOMMANDS.includes(sub))
10556
11232
  return sub;
@@ -10642,6 +11318,10 @@ async function main() {
10642
11318
  process.exit(1);
10643
11319
  }
10644
11320
  const authMode = process.env.CANOPY_AUTH_MODE || "dev";
11321
+ if (!isKnownAuthMode(authMode)) {
11322
+ console.error(`Unknown CANOPY_AUTH_MODE "${authMode}" \u2014 expected one of: ${KNOWN_AUTH_MODES.join(", ")}. No auth plugin was loaded, so the auth cache will NOT be refreshed.`);
11323
+ process.exitCode = 1;
11324
+ }
10645
11325
  let authPlugin;
10646
11326
  try {
10647
11327
  if (authMode === "clerk") {
@@ -10753,6 +11433,8 @@ if (isDirectRun) {
10753
11433
  });
10754
11434
  }
10755
11435
  export {
11436
+ KNOWN_AUTH_MODES,
11437
+ isKnownAuthMode,
10756
11438
  parseArgs,
10757
11439
  parseAuthFlag,
10758
11440
  parseDualBuildFlag,