canopycms 0.0.61-int.75 → 0.0.61

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 (271) hide show
  1. package/dist/ai/handler.js +1 -1
  2. package/dist/ai/handler.js.map +1 -1
  3. package/dist/api/assets.d.ts.map +1 -1
  4. package/dist/api/assets.js +36 -4
  5. package/dist/api/assets.js.map +1 -1
  6. package/dist/api/branch-status.d.ts.map +1 -1
  7. package/dist/api/branch-status.js +56 -0
  8. package/dist/api/branch-status.js.map +1 -1
  9. package/dist/api/branch-withdraw.d.ts.map +1 -1
  10. package/dist/api/branch-withdraw.js +14 -3
  11. package/dist/api/branch-withdraw.js.map +1 -1
  12. package/dist/api/branch.d.ts +31 -2
  13. package/dist/api/branch.d.ts.map +1 -1
  14. package/dist/api/branch.js +252 -13
  15. package/dist/api/branch.js.map +1 -1
  16. package/dist/api/content.d.ts.map +1 -1
  17. package/dist/api/content.js +12 -4
  18. package/dist/api/content.js.map +1 -1
  19. package/dist/api/entries.d.ts.map +1 -1
  20. package/dist/api/entries.js +11 -9
  21. package/dist/api/entries.js.map +1 -1
  22. package/dist/api/guards.d.ts.map +1 -1
  23. package/dist/api/guards.js +12 -5
  24. package/dist/api/guards.js.map +1 -1
  25. package/dist/api/permissions.d.ts +2 -2
  26. package/dist/api/permissions.d.ts.map +1 -1
  27. package/dist/api/permissions.js +51 -3
  28. package/dist/api/permissions.js.map +1 -1
  29. package/dist/api/reference-options.d.ts.map +1 -1
  30. package/dist/api/reference-options.js +10 -17
  31. package/dist/api/reference-options.js.map +1 -1
  32. package/dist/api/resolve-references.d.ts.map +1 -1
  33. package/dist/api/resolve-references.js +3 -1
  34. package/dist/api/resolve-references.js.map +1 -1
  35. package/dist/api/schema.d.ts.map +1 -1
  36. package/dist/api/schema.js +3 -2
  37. package/dist/api/schema.js.map +1 -1
  38. package/dist/api/settings-helpers.d.ts.map +1 -1
  39. package/dist/api/settings-helpers.js +6 -5
  40. package/dist/api/settings-helpers.js.map +1 -1
  41. package/dist/assets/pipeline.d.ts +11 -2
  42. package/dist/assets/pipeline.d.ts.map +1 -1
  43. package/dist/assets/pipeline.js +114 -2
  44. package/dist/assets/pipeline.js.map +1 -1
  45. package/dist/assets/transform-directives.d.ts +18 -0
  46. package/dist/assets/transform-directives.d.ts.map +1 -1
  47. package/dist/assets/transform-directives.js +18 -0
  48. package/dist/assets/transform-directives.js.map +1 -1
  49. package/dist/assets/transform.d.ts.map +1 -1
  50. package/dist/assets/transform.js +1 -10
  51. package/dist/assets/transform.js.map +1 -1
  52. package/dist/auth/types.d.ts +29 -0
  53. package/dist/auth/types.d.ts.map +1 -1
  54. package/dist/authorization/index.d.ts +1 -1
  55. package/dist/authorization/index.d.ts.map +1 -1
  56. package/dist/authorization/index.js +1 -1
  57. package/dist/authorization/index.js.map +1 -1
  58. package/dist/authorization/protected-branch.d.ts +75 -0
  59. package/dist/authorization/protected-branch.d.ts.map +1 -1
  60. package/dist/authorization/protected-branch.js +39 -0
  61. package/dist/authorization/protected-branch.js.map +1 -1
  62. package/dist/branch-registry.d.ts.map +1 -1
  63. package/dist/branch-registry.js +5 -1
  64. package/dist/branch-registry.js.map +1 -1
  65. package/dist/branch-schema-cache.d.ts.map +1 -1
  66. package/dist/branch-schema-cache.js +9 -0
  67. package/dist/branch-schema-cache.js.map +1 -1
  68. package/dist/build/generate-ai-content.js +1 -1
  69. package/dist/build/generate-ai-content.js.map +1 -1
  70. package/dist/cli/cli.js +1442 -828
  71. package/dist/cli/generate-ai-content.js +404 -136
  72. package/dist/cli/init.d.ts +8 -2
  73. package/dist/cli/init.d.ts.map +1 -1
  74. package/dist/cli/init.js +440 -151
  75. package/dist/cli/init.js.map +1 -1
  76. package/dist/cli/project-detect.d.ts +89 -0
  77. package/dist/cli/project-detect.d.ts.map +1 -0
  78. package/dist/cli/project-detect.js +240 -0
  79. package/dist/cli/project-detect.js.map +1 -0
  80. package/dist/cli/template-files/Dockerfile.cms.template +11 -6
  81. package/dist/cli/template-files/cdk-app.ts.template +82 -0
  82. package/dist/cli/template-files/cdk.json.template +9 -0
  83. package/dist/cli/template-files/cms-stack.ts.template +124 -0
  84. package/dist/cli/template-files/deploy-cms.yml.template +125 -13
  85. package/dist/cli/template-files/dockerignore.template +2 -1
  86. package/dist/cli/templates.d.ts +26 -2
  87. package/dist/cli/templates.d.ts.map +1 -1
  88. package/dist/cli/templates.js +36 -4
  89. package/dist/cli/templates.js.map +1 -1
  90. package/dist/config/schemas/config.d.ts +2 -3
  91. package/dist/config/schemas/config.d.ts.map +1 -1
  92. package/dist/config/schemas/config.js +21 -1
  93. package/dist/config/schemas/config.js.map +1 -1
  94. package/dist/config/validation.d.ts +9 -0
  95. package/dist/config/validation.d.ts.map +1 -1
  96. package/dist/config/validation.js +22 -12
  97. package/dist/config/validation.js.map +1 -1
  98. package/dist/content-reader.js +1 -1
  99. package/dist/content-reader.js.map +1 -1
  100. package/dist/content-store.d.ts +11 -0
  101. package/dist/content-store.d.ts.map +1 -1
  102. package/dist/content-store.js +2 -1
  103. package/dist/content-store.js.map +1 -1
  104. package/dist/dev-content-watcher.d.ts.map +1 -1
  105. package/dist/dev-content-watcher.js +4 -1
  106. package/dist/dev-content-watcher.js.map +1 -1
  107. package/dist/editor/BranchManager.d.ts +15 -0
  108. package/dist/editor/BranchManager.d.ts.map +1 -1
  109. package/dist/editor/BranchManager.js +43 -9
  110. package/dist/editor/BranchManager.js.map +1 -1
  111. package/dist/editor/CanopyEditor.d.ts.map +1 -1
  112. package/dist/editor/CanopyEditor.js +2 -2
  113. package/dist/editor/CanopyEditor.js.map +1 -1
  114. package/dist/editor/Editor.d.ts.map +1 -1
  115. package/dist/editor/Editor.js +110 -48
  116. package/dist/editor/Editor.js.map +1 -1
  117. package/dist/editor/admin/SystemHealthPanel.d.ts.map +1 -1
  118. package/dist/editor/admin/SystemHealthPanel.js +9 -8
  119. package/dist/editor/admin/SystemHealthPanel.js.map +1 -1
  120. package/dist/editor/components/EditorHeader.d.ts +6 -0
  121. package/dist/editor/components/EditorHeader.d.ts.map +1 -1
  122. package/dist/editor/components/EditorHeader.js +76 -23
  123. package/dist/editor/components/EditorHeader.js.map +1 -1
  124. package/dist/editor/components/EntryCreateModal.d.ts.map +1 -1
  125. package/dist/editor/components/EntryCreateModal.js +21 -6
  126. package/dist/editor/components/EntryCreateModal.js.map +1 -1
  127. package/dist/editor/context/SWRProvider.d.ts +31 -0
  128. package/dist/editor/context/SWRProvider.d.ts.map +1 -0
  129. package/dist/editor/context/SWRProvider.js +12 -0
  130. package/dist/editor/context/SWRProvider.js.map +1 -0
  131. package/dist/editor/context/index.d.ts +3 -0
  132. package/dist/editor/context/index.d.ts.map +1 -1
  133. package/dist/editor/context/index.js +3 -0
  134. package/dist/editor/context/index.js.map +1 -1
  135. package/dist/editor/editor-utils.d.ts.map +1 -1
  136. package/dist/editor/editor-utils.js +8 -2
  137. package/dist/editor/editor-utils.js.map +1 -1
  138. package/dist/editor/fields/ImageField.d.ts.map +1 -1
  139. package/dist/editor/fields/ImageField.js +27 -3
  140. package/dist/editor/fields/ImageField.js.map +1 -1
  141. package/dist/editor/fields/ReferenceField.d.ts.map +1 -1
  142. package/dist/editor/fields/ReferenceField.js +52 -31
  143. package/dist/editor/fields/ReferenceField.js.map +1 -1
  144. package/dist/editor/hooks/__test__/test-utils.d.ts +38 -3
  145. package/dist/editor/hooks/__test__/test-utils.d.ts.map +1 -1
  146. package/dist/editor/hooks/__test__/test-utils.js +50 -4
  147. package/dist/editor/hooks/__test__/test-utils.js.map +1 -1
  148. package/dist/editor/hooks/index.d.ts +4 -1
  149. package/dist/editor/hooks/index.d.ts.map +1 -1
  150. package/dist/editor/hooks/index.js +4 -1
  151. package/dist/editor/hooks/index.js.map +1 -1
  152. package/dist/editor/hooks/useBranchManager.d.ts +21 -1
  153. package/dist/editor/hooks/useBranchManager.d.ts.map +1 -1
  154. package/dist/editor/hooks/useBranchManager.js +68 -32
  155. package/dist/editor/hooks/useBranchManager.js.map +1 -1
  156. package/dist/editor/hooks/useBranchesData.d.ts +57 -0
  157. package/dist/editor/hooks/useBranchesData.d.ts.map +1 -0
  158. package/dist/editor/hooks/useBranchesData.js +69 -0
  159. package/dist/editor/hooks/useBranchesData.js.map +1 -0
  160. package/dist/editor/hooks/useCommentSystem.d.ts.map +1 -1
  161. package/dist/editor/hooks/useCommentSystem.js +24 -22
  162. package/dist/editor/hooks/useCommentSystem.js.map +1 -1
  163. package/dist/editor/hooks/useCommentsData.d.ts +29 -0
  164. package/dist/editor/hooks/useCommentsData.d.ts.map +1 -0
  165. package/dist/editor/hooks/useCommentsData.js +40 -0
  166. package/dist/editor/hooks/useCommentsData.js.map +1 -0
  167. package/dist/editor/hooks/useDraftManager.d.ts.map +1 -1
  168. package/dist/editor/hooks/useDraftManager.js +32 -18
  169. package/dist/editor/hooks/useDraftManager.js.map +1 -1
  170. package/dist/editor/hooks/useEntriesData.d.ts +35 -0
  171. package/dist/editor/hooks/useEntriesData.d.ts.map +1 -0
  172. package/dist/editor/hooks/useEntriesData.js +96 -0
  173. package/dist/editor/hooks/useEntriesData.js.map +1 -0
  174. package/dist/editor/hooks/useEntryManager.d.ts +8 -15
  175. package/dist/editor/hooks/useEntryManager.d.ts.map +1 -1
  176. package/dist/editor/hooks/useEntryManager.js +259 -117
  177. package/dist/editor/hooks/useEntryManager.js.map +1 -1
  178. package/dist/editor/hooks/usePermissionManager.d.ts +2 -2
  179. package/dist/editor/hooks/usePermissionManager.d.ts.map +1 -1
  180. package/dist/editor/hooks/usePermissionManager.js +11 -9
  181. package/dist/editor/hooks/usePermissionManager.js.map +1 -1
  182. package/dist/editor/hooks/useReferenceResolution.d.ts.map +1 -1
  183. package/dist/editor/hooks/useReferenceResolution.js +24 -1
  184. package/dist/editor/hooks/useReferenceResolution.js.map +1 -1
  185. package/dist/editor/media/MediaLibraryBody.d.ts.map +1 -1
  186. package/dist/editor/media/MediaLibraryBody.js +12 -2
  187. package/dist/editor/media/MediaLibraryBody.js.map +1 -1
  188. package/dist/editor/permission-manager/GroupSelector.d.ts.map +1 -1
  189. package/dist/editor/permission-manager/GroupSelector.js +7 -2
  190. package/dist/editor/permission-manager/GroupSelector.js.map +1 -1
  191. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +3 -3
  192. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts.map +1 -1
  193. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +1 -0
  194. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js.map +1 -1
  195. package/dist/editor/permission-manager/types.d.ts +6 -4
  196. package/dist/editor/permission-manager/types.d.ts.map +1 -1
  197. package/dist/editor/test-setup.js +8 -0
  198. package/dist/editor/test-setup.js.map +1 -1
  199. package/dist/git-manager.d.ts +126 -9
  200. package/dist/git-manager.d.ts.map +1 -1
  201. package/dist/git-manager.js +251 -27
  202. package/dist/git-manager.js.map +1 -1
  203. package/dist/github-service.d.ts.map +1 -1
  204. package/dist/github-service.js +13 -7
  205. package/dist/github-service.js.map +1 -1
  206. package/dist/http/handler.d.ts.map +1 -1
  207. package/dist/http/handler.js +8 -2
  208. package/dist/http/handler.js.map +1 -1
  209. package/dist/operating-mode/client-unsafe-strategy.d.ts.map +1 -1
  210. package/dist/operating-mode/client-unsafe-strategy.js +7 -8
  211. package/dist/operating-mode/client-unsafe-strategy.js.map +1 -1
  212. package/dist/operating-mode/deployment-name.d.ts +28 -0
  213. package/dist/operating-mode/deployment-name.d.ts.map +1 -0
  214. package/dist/operating-mode/deployment-name.js +78 -0
  215. package/dist/operating-mode/deployment-name.js.map +1 -0
  216. package/dist/operating-mode/index.d.ts +5 -1
  217. package/dist/operating-mode/index.d.ts.map +1 -1
  218. package/dist/operating-mode/index.js +7 -1
  219. package/dist/operating-mode/index.js.map +1 -1
  220. package/dist/operating-mode/types.d.ts +13 -3
  221. package/dist/operating-mode/types.d.ts.map +1 -1
  222. package/dist/paths/branch-name.d.ts +38 -0
  223. package/dist/paths/branch-name.d.ts.map +1 -1
  224. package/dist/paths/branch-name.js +46 -0
  225. package/dist/paths/branch-name.js.map +1 -1
  226. package/dist/paths/index.d.ts +1 -1
  227. package/dist/paths/index.d.ts.map +1 -1
  228. package/dist/paths/index.js +1 -1
  229. package/dist/paths/index.js.map +1 -1
  230. package/dist/reference-resolver.d.ts +6 -2
  231. package/dist/reference-resolver.d.ts.map +1 -1
  232. package/dist/reference-resolver.js +8 -1
  233. package/dist/reference-resolver.js.map +1 -1
  234. package/dist/schema/meta-loader.d.ts.map +1 -1
  235. package/dist/schema/meta-loader.js +10 -2
  236. package/dist/schema/meta-loader.js.map +1 -1
  237. package/dist/schema/schema-store.d.ts +11 -2
  238. package/dist/schema/schema-store.d.ts.map +1 -1
  239. package/dist/schema/schema-store.js +23 -11
  240. package/dist/schema/schema-store.js.map +1 -1
  241. package/dist/settings-workspace.d.ts +0 -15
  242. package/dist/settings-workspace.d.ts.map +1 -1
  243. package/dist/settings-workspace.js +97 -1
  244. package/dist/settings-workspace.js.map +1 -1
  245. package/dist/types.d.ts +63 -2
  246. package/dist/types.d.ts.map +1 -1
  247. package/dist/utils/debug.d.ts +11 -0
  248. package/dist/utils/debug.d.ts.map +1 -1
  249. package/dist/utils/debug.js +12 -1
  250. package/dist/utils/debug.js.map +1 -1
  251. package/dist/utils/git.d.ts +38 -0
  252. package/dist/utils/git.d.ts.map +1 -1
  253. package/dist/utils/git.js +69 -0
  254. package/dist/utils/git.js.map +1 -1
  255. package/dist/utils/logger.d.ts +71 -0
  256. package/dist/utils/logger.d.ts.map +1 -0
  257. package/dist/utils/logger.js +85 -0
  258. package/dist/utils/logger.js.map +1 -0
  259. package/dist/validation/entry-type-reference-validator.d.ts +33 -0
  260. package/dist/validation/entry-type-reference-validator.d.ts.map +1 -0
  261. package/dist/validation/entry-type-reference-validator.js +110 -0
  262. package/dist/validation/entry-type-reference-validator.js.map +1 -0
  263. package/dist/worker/cms-worker.d.ts +232 -3
  264. package/dist/worker/cms-worker.d.ts.map +1 -1
  265. package/dist/worker/cms-worker.js +856 -87
  266. package/dist/worker/cms-worker.js.map +1 -1
  267. package/dist/worker/log.d.ts +61 -0
  268. package/dist/worker/log.d.ts.map +1 -0
  269. package/dist/worker/log.js +74 -0
  270. package/dist/worker/log.js.map +1 -0
  271. package/package.json +3 -1
@@ -38,6 +38,21 @@ var init_normalize = __esm({
38
38
  }
39
39
  });
40
40
 
41
+ // dist/utils/logger.js
42
+ function canopyLogWarn(...args) {
43
+ active.warn(...args);
44
+ }
45
+ function canopyLogError(...args) {
46
+ active.error(...args);
47
+ }
48
+ var active;
49
+ var init_logger = __esm({
50
+ "dist/utils/logger.js"() {
51
+ "use strict";
52
+ active = console;
53
+ }
54
+ });
55
+
41
56
  // dist/paths/types.js
42
57
  var ROOT_COLLECTION_ID;
43
58
  var init_types = __esm({
@@ -150,100 +165,6 @@ var init_flatten = __esm({
150
165
  }
151
166
  });
152
167
 
153
- // dist/operating-mode/client-safe-strategy.js
154
- function clientOperatingStrategy(mode) {
155
- const cached = clientStrategyCache.get(mode);
156
- if (cached)
157
- return cached;
158
- let strategy;
159
- switch (mode) {
160
- case "prod":
161
- strategy = new ProdClientSafeStrategy();
162
- break;
163
- case "dev":
164
- strategy = new DevClientSafeStrategy();
165
- break;
166
- default: {
167
- const _exhaustive = mode;
168
- throw new Error(`Unknown operating mode: ${_exhaustive}`);
169
- }
170
- }
171
- clientStrategyCache.set(mode, strategy);
172
- return strategy;
173
- }
174
- function clearClientStrategyCache() {
175
- clientStrategyCache.clear();
176
- }
177
- var ProdClientSafeStrategy, DevClientSafeStrategy, clientStrategyCache;
178
- var init_client_safe_strategy = __esm({
179
- "dist/operating-mode/client-safe-strategy.js"() {
180
- "use strict";
181
- ProdClientSafeStrategy = class {
182
- constructor() {
183
- this.mode = "prod";
184
- }
185
- // UI Feature Flags
186
- supportsBranching() {
187
- return true;
188
- }
189
- supportsStatusBadge() {
190
- return true;
191
- }
192
- supportsComments() {
193
- return true;
194
- }
195
- supportsPullRequests() {
196
- return true;
197
- }
198
- // Simple Data
199
- getPermissionsFileName() {
200
- return "permissions.json";
201
- }
202
- getGroupsFileName() {
203
- return "groups.json";
204
- }
205
- shouldCommit() {
206
- return true;
207
- }
208
- shouldPush() {
209
- return true;
210
- }
211
- };
212
- DevClientSafeStrategy = class {
213
- constructor() {
214
- this.mode = "dev";
215
- }
216
- // UI Feature Flags
217
- supportsBranching() {
218
- return true;
219
- }
220
- supportsStatusBadge() {
221
- return true;
222
- }
223
- supportsComments() {
224
- return true;
225
- }
226
- supportsPullRequests() {
227
- return false;
228
- }
229
- // Simple Data
230
- getPermissionsFileName() {
231
- return "permissions.json";
232
- }
233
- getGroupsFileName() {
234
- return "groups.json";
235
- }
236
- shouldCommit() {
237
- return true;
238
- }
239
- shouldPush() {
240
- return true;
241
- }
242
- };
243
- clientStrategyCache = /* @__PURE__ */ new Map();
244
- }
245
- });
246
-
247
168
  // dist/config/types.js
248
169
  var primitiveFieldTypes, fieldTypes;
249
170
  var init_types2 = __esm({
@@ -502,6 +423,16 @@ var init_config = __esm({
502
423
  allowNetworkRemoteInProd: z5.boolean().optional(),
503
424
  settingsBranch: z5.string().optional(),
504
425
  autoCreateSettingsPR: z5.boolean().optional(),
426
+ // Deliberately `deploymentNameSchema.optional()`, NOT `deploymentNameSchema` alone.
427
+ // deploymentNameSchema.default('prod') would make `parse(undefined)` resolve to
428
+ // the literal 'prod' instead of staying `undefined` — collapsing the
429
+ // env > config > modeDefault precedence chain that resolveDeploymentName
430
+ // (operating-mode/deployment-name.ts) implements: config would then always
431
+ // "win" over modeDefault (masking dev's real default of 'local'), and the env
432
+ // var would just be racing config's baked-in 'prod' instead of a true absence.
433
+ // `.optional()` here is what keeps an omitted deploymentName reaching
434
+ // resolveDeploymentName as `undefined`, so it can fall through to modeDefault.
435
+ // Do not "fix" this by removing `.optional()`.
505
436
  deploymentName: deploymentNameSchema.optional(),
506
437
  contentRoot: contentRootSchema.default("content"),
507
438
  sourceRoot: sourceRootSchema.optional(),
@@ -515,6 +446,154 @@ var init_config = __esm({
515
446
  }
516
447
  });
517
448
 
449
+ // dist/config/validation.js
450
+ var forEachReferenceField;
451
+ var init_validation = __esm({
452
+ "dist/config/validation.js"() {
453
+ "use strict";
454
+ init_config();
455
+ init_flatten();
456
+ forEachReferenceField = (fields, visit) => {
457
+ if (!Array.isArray(fields))
458
+ return;
459
+ for (const field of fields) {
460
+ const f = field;
461
+ if (f?.type === "reference") {
462
+ visit(f);
463
+ }
464
+ if (f?.type === "group" || f?.type === "object") {
465
+ forEachReferenceField(f.fields, visit);
466
+ }
467
+ if (f?.type === "block" && Array.isArray(f.templates)) {
468
+ for (const template of f.templates) {
469
+ forEachReferenceField(template.fields, visit);
470
+ }
471
+ }
472
+ }
473
+ };
474
+ }
475
+ });
476
+
477
+ // dist/operating-mode/client-safe-strategy.js
478
+ function clientOperatingStrategy(mode) {
479
+ const cached = clientStrategyCache.get(mode);
480
+ if (cached)
481
+ return cached;
482
+ let strategy;
483
+ switch (mode) {
484
+ case "prod":
485
+ strategy = new ProdClientSafeStrategy();
486
+ break;
487
+ case "dev":
488
+ strategy = new DevClientSafeStrategy();
489
+ break;
490
+ default: {
491
+ const _exhaustive = mode;
492
+ throw new Error(`Unknown operating mode: ${_exhaustive}`);
493
+ }
494
+ }
495
+ clientStrategyCache.set(mode, strategy);
496
+ return strategy;
497
+ }
498
+ function clearClientStrategyCache() {
499
+ clientStrategyCache.clear();
500
+ }
501
+ var ProdClientSafeStrategy, DevClientSafeStrategy, clientStrategyCache;
502
+ var init_client_safe_strategy = __esm({
503
+ "dist/operating-mode/client-safe-strategy.js"() {
504
+ "use strict";
505
+ ProdClientSafeStrategy = class {
506
+ constructor() {
507
+ this.mode = "prod";
508
+ }
509
+ // UI Feature Flags
510
+ supportsBranching() {
511
+ return true;
512
+ }
513
+ supportsStatusBadge() {
514
+ return true;
515
+ }
516
+ supportsComments() {
517
+ return true;
518
+ }
519
+ supportsPullRequests() {
520
+ return true;
521
+ }
522
+ // Simple Data
523
+ getPermissionsFileName() {
524
+ return "permissions.json";
525
+ }
526
+ getGroupsFileName() {
527
+ return "groups.json";
528
+ }
529
+ shouldCommit() {
530
+ return true;
531
+ }
532
+ shouldPush() {
533
+ return true;
534
+ }
535
+ };
536
+ DevClientSafeStrategy = class {
537
+ constructor() {
538
+ this.mode = "dev";
539
+ }
540
+ // UI Feature Flags
541
+ supportsBranching() {
542
+ return true;
543
+ }
544
+ supportsStatusBadge() {
545
+ return true;
546
+ }
547
+ supportsComments() {
548
+ return true;
549
+ }
550
+ supportsPullRequests() {
551
+ return false;
552
+ }
553
+ // Simple Data
554
+ getPermissionsFileName() {
555
+ return "permissions.json";
556
+ }
557
+ getGroupsFileName() {
558
+ return "groups.json";
559
+ }
560
+ shouldCommit() {
561
+ return true;
562
+ }
563
+ shouldPush() {
564
+ return true;
565
+ }
566
+ };
567
+ clientStrategyCache = /* @__PURE__ */ new Map();
568
+ }
569
+ });
570
+
571
+ // dist/operating-mode/deployment-name.js
572
+ function resolveDeploymentName(config, modeDefault) {
573
+ const envValue = process.env.CANOPYCMS_DEPLOYMENT_NAME?.trim();
574
+ const configValue = config.deploymentName;
575
+ if (envValue && configValue && envValue !== configValue && !warned) {
576
+ canopyLogWarn(`CanopyCMS: CANOPYCMS_DEPLOYMENT_NAME ("${envValue}") differs from config.deploymentName ("${configValue}") \u2014 using the env var (infra-stamped env wins over shared-repo config by design; see resolveDeploymentName's doc comment). Update config.deploymentName to match if this mismatch was unintentional.`);
577
+ warned = true;
578
+ }
579
+ const resolved = envValue || configValue || modeDefault;
580
+ if (!isValidDeploymentName(resolved)) {
581
+ const source = envValue ? "CANOPYCMS_DEPLOYMENT_NAME" : configValue ? "config.deploymentName" : "the mode default";
582
+ throw new Error(`CanopyCMS: invalid deploymentName ${JSON.stringify(resolved)} (from ${source}). It is interpolated into the settings branch name (canopycms-settings-<deploymentName>), so it must start with a letter or digit, contain only letters, digits, '.', '_' or '-', and must not contain '..' or end with '.' or '.lock'.`);
583
+ }
584
+ return resolved;
585
+ }
586
+ var warned, VALID_DEPLOYMENT_NAME, isValidDeploymentName;
587
+ var init_deployment_name = __esm({
588
+ "dist/operating-mode/deployment-name.js"() {
589
+ "use strict";
590
+ init_logger();
591
+ warned = false;
592
+ VALID_DEPLOYMENT_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
593
+ isValidDeploymentName = (name) => VALID_DEPLOYMENT_NAME.test(name) && !name.includes("..") && !name.endsWith(".") && !name.endsWith(".lock");
594
+ }
595
+ });
596
+
518
597
  // dist/config/schemas/permissions.js
519
598
  import { z as z6 } from "zod";
520
599
  var permissionTargetSchema, pathPermissionSchema;
@@ -534,15 +613,6 @@ var init_permissions = __esm({
534
613
  }
535
614
  });
536
615
 
537
- // dist/config/validation.js
538
- var init_validation = __esm({
539
- "dist/config/validation.js"() {
540
- "use strict";
541
- init_config();
542
- init_flatten();
543
- }
544
- });
545
-
546
616
  // dist/config/helpers.js
547
617
  var init_helpers = __esm({
548
618
  "dist/config/helpers.js"() {
@@ -597,6 +667,7 @@ var init_client_unsafe_strategy = __esm({
597
667
  "dist/operating-mode/client-unsafe-strategy.js"() {
598
668
  "use strict";
599
669
  init_client_safe_strategy();
670
+ init_deployment_name();
600
671
  init_config2();
601
672
  ProdStrategy = class extends ProdClientSafeStrategy {
602
673
  // All client-safe methods inherited automatically from ProdClientSafeStrategy:
@@ -607,8 +678,8 @@ var init_client_unsafe_strategy = __esm({
607
678
  getWorkspaceRoot(_sourceRoot) {
608
679
  return path6.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE);
609
680
  }
610
- getContentRoot(sourceRoot) {
611
- return path6.resolve(sourceRoot ?? process.cwd(), "content");
681
+ getContentRoot(contentRoot, sourceRoot) {
682
+ return path6.resolve(sourceRoot ?? process.cwd(), contentRoot);
612
683
  }
613
684
  getContentBranchesRoot(sourceRoot) {
614
685
  return path6.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
@@ -639,8 +710,7 @@ var init_client_unsafe_strategy = __esm({
639
710
  getSettingsBranchName(config) {
640
711
  if (config.settingsBranch)
641
712
  return config.settingsBranch;
642
- const deploymentName = config.deploymentName ?? "prod";
643
- return `canopycms-settings-${deploymentName}`;
713
+ return `canopycms-settings-${resolveDeploymentName(config, "prod")}`;
644
714
  }
645
715
  getSettingsRoot(sourceRoot) {
646
716
  return path6.join(this.getWorkspaceRoot(sourceRoot), "settings");
@@ -662,8 +732,8 @@ var init_client_unsafe_strategy = __esm({
662
732
  getWorkspaceRoot(sourceRoot) {
663
733
  return path6.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
664
734
  }
665
- getContentRoot(sourceRoot) {
666
- return path6.resolve(sourceRoot ?? process.cwd(), "content");
735
+ getContentRoot(contentRoot, sourceRoot) {
736
+ return path6.resolve(sourceRoot ?? process.cwd(), contentRoot);
667
737
  }
668
738
  getContentBranchesRoot(sourceRoot) {
669
739
  return path6.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
@@ -693,8 +763,7 @@ var init_client_unsafe_strategy = __esm({
693
763
  getSettingsBranchName(config) {
694
764
  if (config.settingsBranch)
695
765
  return config.settingsBranch;
696
- const deploymentName = config.deploymentName ?? "local";
697
- return `canopycms-settings-${deploymentName}`;
766
+ return `canopycms-settings-${resolveDeploymentName(config, "local")}`;
698
767
  }
699
768
  getSettingsRoot(sourceRoot) {
700
769
  return path6.join(this.getWorkspaceRoot(sourceRoot), "settings");
@@ -718,13 +787,15 @@ __export(operating_mode_exports, {
718
787
  clearClientStrategyCache: () => clearClientStrategyCache,
719
788
  clearStrategyCache: () => clearStrategyCache,
720
789
  clientOperatingStrategy: () => clientOperatingStrategy,
721
- operatingStrategy: () => operatingStrategy
790
+ operatingStrategy: () => operatingStrategy,
791
+ resolveDeploymentName: () => resolveDeploymentName
722
792
  });
723
793
  var init_operating_mode = __esm({
724
794
  "dist/operating-mode/index.js"() {
725
795
  "use strict";
726
796
  init_client_safe_strategy();
727
797
  init_client_unsafe_strategy();
798
+ init_deployment_name();
728
799
  }
729
800
  });
730
801
 
@@ -1232,10 +1303,21 @@ var DebugLogger = class {
1232
1303
  const minLevel = this.options.minLevel ?? "DEBUG";
1233
1304
  return LOG_LEVELS[level] >= LOG_LEVELS[minLevel];
1234
1305
  }
1306
+ /**
1307
+ * The ISO-8601 timestamp leads the line BARE - not wrapped in brackets as it
1308
+ * once was. `CANOPYCMS_DEBUG=true` can be set on the EC2 worker, whose stdout
1309
+ * appends to /var/log/canopy-worker/worker.log; the CloudWatch agent's
1310
+ * `multi_line_start_pattern` matches the timestamp at the START of a line
1311
+ * (see worker/log.ts's INVARIANT and cms-service.ts's agent config), so a
1312
+ * leading `[` meant every debug line was silently folded into the previous
1313
+ * event instead of starting its own. The bracketed `[prefix:category]` and
1314
+ * `[level]` fields still follow, so the human-readable shape is unchanged
1315
+ * apart from those two characters.
1316
+ */
1235
1317
  formatMessage(level, category, message) {
1236
1318
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1237
1319
  const prefix = this.options.prefix ?? "CanopyCMS";
1238
- return `[${timestamp}] [${prefix}:${category}] [${level}] ${message}`;
1320
+ return `${timestamp} [${prefix}:${category}] [${level}] ${message}`;
1239
1321
  }
1240
1322
  debug(category, message, data) {
1241
1323
  if (this.shouldLog("DEBUG")) {
@@ -1350,6 +1432,7 @@ import { promises as fs4 } from "fs";
1350
1432
  import { join } from "pathe";
1351
1433
  import { z } from "zod";
1352
1434
  import chokidar from "chokidar";
1435
+ init_logger();
1353
1436
  var entryTypeMetaSchema = z.object({
1354
1437
  name: z.string().min(1),
1355
1438
  format: z.enum(["md", "mdx", "json", "yaml"]),
@@ -1407,7 +1490,7 @@ async function scanForCollectionMeta(baseDir, relativePath = "") {
1407
1490
  collections.push(...nestedCollections);
1408
1491
  } catch (err) {
1409
1492
  if (err.code !== "ENOENT") {
1410
- console.error(`Error loading ${metaPath}:`, err);
1493
+ canopyLogError(`Error loading ${metaPath}:`, err);
1411
1494
  throw new Error(`Invalid .collection.json in ${folderPath}: ${err.message}`);
1412
1495
  }
1413
1496
  const nestedCollections = await scanForCollectionMeta(absolutePath, folderPath);
@@ -1527,6 +1610,74 @@ function isValidSchema(schema) {
1527
1610
  // dist/branch-schema-cache.js
1528
1611
  init_flatten();
1529
1612
 
1613
+ // dist/validation/entry-type-reference-validator.js
1614
+ init_validation();
1615
+ var editDistance = (a, b) => {
1616
+ let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
1617
+ for (let i = 1; i <= a.length; i++) {
1618
+ const curr = [i];
1619
+ for (let j = 1; j <= b.length; j++) {
1620
+ curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
1621
+ }
1622
+ prev = curr;
1623
+ }
1624
+ return prev[b.length];
1625
+ };
1626
+ var closestName = (name, known) => {
1627
+ let best;
1628
+ let bestDistance = Infinity;
1629
+ for (const candidate of known) {
1630
+ const distance = editDistance(name.toLowerCase(), candidate.toLowerCase());
1631
+ if (distance < bestDistance) {
1632
+ bestDistance = distance;
1633
+ best = candidate;
1634
+ }
1635
+ }
1636
+ const threshold = Math.max(2, Math.floor(name.length / 3));
1637
+ return best !== void 0 && bestDistance <= threshold ? best : void 0;
1638
+ };
1639
+ var forEachEntryType = (schema, visit) => {
1640
+ const walk = (node, nodePath) => {
1641
+ for (const entryType of node.entries ?? []) {
1642
+ visit(entryType, nodePath);
1643
+ }
1644
+ for (const child of node.collections ?? []) {
1645
+ walk(child, child.path ?? (nodePath ? `${nodePath}/${child.name}` : child.name));
1646
+ }
1647
+ };
1648
+ walk(schema, "");
1649
+ };
1650
+ var collectEntryTypeNames = (schema) => {
1651
+ const names = /* @__PURE__ */ new Set();
1652
+ forEachEntryType(schema, (entryType) => {
1653
+ if (typeof entryType.name === "string" && entryType.name.length > 0) {
1654
+ names.add(entryType.name);
1655
+ }
1656
+ });
1657
+ return names;
1658
+ };
1659
+ var validateReferenceEntryTypes = (schema) => {
1660
+ const known = collectEntryTypeNames(schema);
1661
+ const knownList = [...known].sort();
1662
+ const issues = [];
1663
+ forEachEntryType(schema, (entryType, collectionPath) => {
1664
+ const location = collectionPath ? `${collectionPath}/${entryType.name}` : entryType.name;
1665
+ forEachReferenceField(entryType.schema, (field) => {
1666
+ const entryTypes = field.entryTypes;
1667
+ if (!Array.isArray(entryTypes))
1668
+ return;
1669
+ const fieldName = typeof field.name === "string" ? field.name : "unknown";
1670
+ for (const value of entryTypes) {
1671
+ if (typeof value !== "string" || known.has(value))
1672
+ continue;
1673
+ const suggestion = closestName(value, knownList);
1674
+ issues.push(`Reference field "${fieldName}" in "${location}" specifies entryType "${value}", which is not defined in any collection.` + (suggestion ? ` Did you mean "${suggestion}"?` : "") + (knownList.length > 0 ? ` Known entry types: ${knownList.join(", ")}.` : " No entry types are defined in this schema."));
1675
+ }
1676
+ });
1677
+ });
1678
+ return issues;
1679
+ };
1680
+
1530
1681
  // dist/build-mode.js
1531
1682
  var isDeployedStatic = (config) => {
1532
1683
  return config.deployedAs === "static";
@@ -1662,6 +1813,11 @@ var BranchSchemaCache = class {
1662
1813
  if (!isValidSchema(result.schema)) {
1663
1814
  throw new Error(`No schema found in ${contentRoot}. Create .collection.json files with references to field schemas defined in your entry schema registry.`);
1664
1815
  }
1816
+ const entryTypeIssues = validateReferenceEntryTypes(result.schema);
1817
+ if (entryTypeIssues.length > 0) {
1818
+ throw new Error(`Invalid reference field entryTypes in ${contentRoot}:
1819
+ ` + entryTypeIssues.map((issue) => ` - ${issue}`).join("\n"));
1820
+ }
1665
1821
  const flatSchema = flattenSchema(result.schema, contentRootName);
1666
1822
  if (!skipDiskCache) {
1667
1823
  const cacheDir = path5.join(branchRoot, ".canopy-meta");
@@ -1888,6 +2044,7 @@ var ContentStore = class {
1888
2044
  this.lastForcedRefreshMs = 0;
1889
2045
  this.indexBuild = null;
1890
2046
  this.root = path8.resolve(root);
2047
+ this.contentRootName = options.contentRootName || "content";
1891
2048
  this.indexFreshnessIntervalMs = options.indexFreshnessIntervalMs ?? DEFAULT_INDEX_FRESHNESS_INTERVAL_MS;
1892
2049
  this.schemaIndex = new Map(flatSchema.map((item) => [item.logicalPath, item]));
1893
2050
  this._idIndex = new ContentIdIndex(this.root);
@@ -1929,7 +2086,7 @@ var ContentStore = class {
1929
2086
  const build = (async () => {
1930
2087
  const diskToken = await readContentIndexGeneration(this.root);
1931
2088
  const fresh = new ContentIdIndex(this.root);
1932
- await fresh.buildFromFilenames("content");
2089
+ await fresh.buildFromFilenames(this.contentRootName);
1933
2090
  return { diskToken, fresh };
1934
2091
  })();
1935
2092
  this.indexBuild = build;
@@ -3718,6 +3875,7 @@ import path12 from "node:path";
3718
3875
  // dist/branch-registry.js
3719
3876
  import fs10 from "node:fs/promises";
3720
3877
  import path10 from "node:path";
3878
+ init_logger();
3721
3879
  var log3 = createDebugLogger({ prefix: "BranchRegistry" });
3722
3880
  var REGISTRY_FILE = "branches.json";
3723
3881
  var REGISTRY_STALE_FILE = "branches.stale.json";
@@ -3870,7 +4028,7 @@ var BranchRegistry = class {
3870
4028
  try {
3871
4029
  meta = await BranchMetadataFileManager.loadOnly(branchRoot);
3872
4030
  } catch (err) {
3873
- console.warn(`CanopyCMS: Skipping branch directory '${entry.name}' during registry scan: ${getErrorMessage(err)}`);
4031
+ canopyLogWarn(`CanopyCMS: Skipping branch directory '${entry.name}' during registry scan: ${getErrorMessage(err)}`);
3874
4032
  continue;
3875
4033
  }
3876
4034
  if (meta) {
@@ -4280,15 +4438,17 @@ async function acquireProvisioningLock(lockTargetDir, lockName) {
4280
4438
  // dist/git-manager.js
4281
4439
  var log5 = createDebugLogger({ prefix: "GitManager" });
4282
4440
  var GIT_ENV_PASSTHROUGH = /^(PATH|HOME|USER|LANG|LC_[A-Z]+|TZ|TMPDIR|GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL|DATE)|GIT_TERMINAL_PROMPT|GIT_TRACE[0-9A-Z_]*)$/;
4441
+ var FORCE_C_LOCALE = { LC_ALL: "C", LANG: "C" };
4283
4442
  function gitChildEnv(overrides) {
4284
4443
  const env = {};
4285
4444
  for (const [key, value] of Object.entries(process.env)) {
4286
4445
  if (value !== void 0 && GIT_ENV_PASSTHROUGH.test(key))
4287
4446
  env[key] = value;
4288
4447
  }
4289
- return { ...env, ...overrides };
4448
+ return { ...env, ...FORCE_C_LOCALE, ...overrides };
4290
4449
  }
4291
4450
  var remoteInitLocks = /* @__PURE__ */ new Map();
4451
+ var GITHUB_TRACKING_REF_PREFIX = "refs/remotes/github/";
4292
4452
  async function ensureGitExcludePattern(repoPath, pattern) {
4293
4453
  const excludePath = path14.join(repoPath, ".git", "info", "exclude");
4294
4454
  await fs14.mkdir(path14.dirname(excludePath), { recursive: true });
@@ -4443,26 +4603,115 @@ var GitManager = class _GitManager {
4443
4603
  await lockPromise;
4444
4604
  }
4445
4605
  /**
4446
- * Whether a bare repository already has a local branch of the given name.
4606
+ * Whether a bare repository already has a branch of the given name, in
4607
+ * EITHER the local-heads namespace (`refs/heads/<branch>`) or the GitHub
4608
+ * tracking namespace (`GITHUB_TRACKING_REF_PREFIX<branch>`).
4609
+ *
4610
+ * Originally checked `refs/heads/*` only, for dev-mode's
4611
+ * `ensureLocalSimulatedRemote` (a simulated remote never gets a tracking
4612
+ * namespace, so that caller only ever needed the local-heads check).
4613
+ * Generalized/promoted to public for api/branch.ts's create-time collision
4614
+ * guard, which also needs the tracking namespace: `syncGit()` fetches
4615
+ * GitHub into `GITHUB_TRACKING_REF_PREFIX*` rather than `refs/heads/*`
4616
+ * directly (see that constant's doc comment above), and
4617
+ * `reconcileTrackedBranches()` (worker/cms-worker.ts) only
4618
+ * non-destructively brings `refs/heads/*` toward what's tracked there — so
4619
+ * a branch that another CanopyCMS deployment sharing this repo (or a
4620
+ * direct push to GitHub) just created can sit in the tracking namespace
4621
+ * for a while before, or without ever, gaining a local head here. Checking
4622
+ * `refs/heads/*` alone would miss exactly the two-deployments-one-repo
4623
+ * collision that guard exists to catch.
4447
4624
  *
4448
4625
  * Runs git with an explicit `--git-dir` instead of a cwd inside the repo:
4449
4626
  * environments with `safe.bareRepository=explicit` (sandboxed/CI git setups)
4450
4627
  * refuse cwd-based discovery of bare repos but expressly allow `--git-dir`.
4451
- * `branch --list` exits 0 whether or not the branch exists (no
4452
- * exception-based control flow — also why `rev-parse --verify --quiet`
4453
- * can't be used: simple-git only fails a task on stderr output, and --quiet
4454
- * suppresses exactly that). A failure here therefore means the remote
4455
- * itself is unreadable and is surfaced, NOT treated as "branch absent" —
4456
- * that would route to the push path against a repo we couldn't even read.
4628
+ * A single `for-each-ref` call checks both candidate refs at once (no
4629
+ * exception-based control flow — `for-each-ref` exits 0 whether or not
4630
+ * either ref exists, same reason the old implementation used `branch
4631
+ * --list` instead of `rev-parse --verify --quiet`: simple-git only fails a
4632
+ * task on stderr output, and `--quiet` suppresses exactly that).
4633
+ * `--end-of-options` guards the ref-name positionals the same way
4634
+ * `push`/`forcePush` below do, since `branch` here can be a sanitized but
4635
+ * otherwise caller-influenced string.
4636
+ *
4637
+ * A failure here therefore means the remote itself is unreadable and is
4638
+ * surfaced, NOT treated as "branch absent" — that would route
4639
+ * `ensureLocalSimulatedRemote` to the push path against a repo it couldn't
4640
+ * even read, and would silently skip api/branch.ts's collision check
4641
+ * instead of letting that caller distinguish "unreadable" from "absent"
4642
+ * and decide what to do.
4457
4643
  */
4458
- static async bareRemoteHasBranch(remotePath, branch) {
4644
+ static async bareRemoteHasBranch(remotePath, branch, options = {}) {
4459
4645
  let output;
4460
4646
  try {
4461
- output = await simpleGit2().raw(["--git-dir", remotePath, "branch", "--list", branch]);
4647
+ output = await simpleGit2().raw([
4648
+ "--git-dir",
4649
+ remotePath,
4650
+ "for-each-ref",
4651
+ "--format=%(refname)",
4652
+ "--end-of-options",
4653
+ `refs/heads/${branch}`,
4654
+ `${GITHUB_TRACKING_REF_PREFIX}${branch}`
4655
+ ]);
4462
4656
  } catch (err) {
4463
- throw new Error(`Cannot inspect simulated remote at ${remotePath}: ${getErrorMessage(err)}`);
4657
+ throw new Error(`Cannot inspect remote mirror at ${remotePath}: ${getErrorMessage(err)}`);
4658
+ }
4659
+ const refs = output.split("\n").map((line) => line.trim()).filter(Boolean);
4660
+ if (options.namespaces === "tracking") {
4661
+ return refs.includes(`${GITHUB_TRACKING_REF_PREFIX}${branch}`);
4464
4662
  }
4465
- return output.split("\n").map((line) => line.replace(/^[*+]\s*/, "").trim()).includes(branch);
4663
+ return refs.includes(`refs/heads/${branch}`) || refs.includes(`${GITHUB_TRACKING_REF_PREFIX}${branch}`);
4664
+ }
4665
+ /**
4666
+ * Delete `refs/heads/<branch>` from a bare local mirror, if present. A
4667
+ * no-op (not an error) when the ref doesn't exist.
4668
+ *
4669
+ * This is the "explicit path" for removing a deleted branch's local head
4670
+ * that the sync loop deliberately is not (see GITHUB_TRACKING_REF_PREFIX's
4671
+ * doc comment: reconcileTrackedBranches never deletes a head). Called by
4672
+ * api/branch.ts's deleteBranchHandler: without it, a deleted branch's head
4673
+ * lives in `remote.git` forever, and the ordinary create -> publish ->
4674
+ * squash-merge -> delete -> reuse-the-name cycle then has the REUSED
4675
+ * branch's first publish rejected non-fast-forward against the stale head
4676
+ * (`GitManager.push()` pushes `branch:branch`, and a squash-merged old tip
4677
+ * is not an ancestor of the new branch) -- a permanent, misleading 409.
4678
+ * Worse, a retried submit skips the local push (clean tree) and enqueues
4679
+ * the worker push of the STALE head, resurrecting the deleted branch's
4680
+ * content on GitHub as an apparent success.
4681
+ *
4682
+ * Deliberately leaves the tracking ref (`GITHUB_TRACKING_REF_PREFIX<branch>`)
4683
+ * alone: that namespace mirrors GitHub's view, and if the branch still
4684
+ * exists on GitHub, the create-time collision check SHOULD keep reporting
4685
+ * it until the remote side is actually gone.
4686
+ *
4687
+ * Same `--git-dir` invocation style as bareRemoteHasBranch above (works
4688
+ * under `safe.bareRepository=explicit`). The existence pre-check makes
4689
+ * "absent" deterministic instead of parsing update-ref's locale-dependent
4690
+ * failure text, and its captured SHA is passed to `update-ref -d` as the
4691
+ * expected old value -- same pattern as reconcileTrackedBranches'
4692
+ * guarded updates (worker/cms-worker.ts): a concurrent Lambda push
4693
+ * re-creating/moving this branch between the read and the delete makes
4694
+ * update-ref throw (surfaced as the caller's best-effort warning) instead
4695
+ * of silently deleting a commit that was just pushed. No
4696
+ * `--end-of-options` on update-ref (older gits don't accept it there);
4697
+ * the ref argument always begins with the literal `refs/heads/` prefix,
4698
+ * so it can never parse as an option.
4699
+ */
4700
+ static async deleteBareRemoteHead(remotePath, branch) {
4701
+ const ref = `refs/heads/${branch}`;
4702
+ const output = await simpleGit2().raw([
4703
+ "--git-dir",
4704
+ remotePath,
4705
+ "for-each-ref",
4706
+ "--format=%(refname) %(objectname)",
4707
+ "--end-of-options",
4708
+ ref
4709
+ ]);
4710
+ const match = output.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => line.split(" ")).find(([refname]) => refname === ref);
4711
+ if (!match)
4712
+ return;
4713
+ const [, sha] = match;
4714
+ await simpleGit2().raw(["--git-dir", remotePath, "update-ref", "-d", ref, sha]);
4466
4715
  }
4467
4716
  /**
4468
4717
  * Push baseBranch from the source repo into the local bare remote via a
@@ -4635,6 +4884,30 @@ var GitManager = class _GitManager {
4635
4884
  }
4636
4885
  return void 0;
4637
4886
  }
4887
+ /**
4888
+ * Check whether a git repository is already initialized at `workspacePath`.
4889
+ *
4890
+ * Uses `rev-parse --git-dir` with `GIT_CEILING_DIRECTORIES` pinned to the
4891
+ * parent directory so a corrupt/missing `.git` can't make git silently
4892
+ * traverse upward and report a false positive from an ancestor repo (the
4893
+ * same protection `initializeWorkspace` relies on below).
4894
+ *
4895
+ * Shared by `initializeWorkspace` (clone-vs-reuse decision) and
4896
+ * `SettingsWorkspaceManager`'s rename guard (settings-workspace.ts), which
4897
+ * must know whether a settings workspace ALREADY exists before touching it —
4898
+ * touching an existing orphan settings branch under a different name wipes
4899
+ * permissions.json/groups.json (see that guard's doc comment).
4900
+ */
4901
+ static async repoExistsAt(workspacePath) {
4902
+ try {
4903
+ const checkGit = simpleGit2({ baseDir: workspacePath });
4904
+ checkGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path14.dirname(workspacePath) }));
4905
+ await checkGit.raw(["rev-parse", "--git-dir"]);
4906
+ return true;
4907
+ } catch {
4908
+ return false;
4909
+ }
4910
+ }
4638
4911
  /**
4639
4912
  * Ensures a git workspace is initialized and ready for use.
4640
4913
  * Handles cloning, remote configuration, and branch checkout/creation.
@@ -4653,13 +4926,8 @@ var GitManager = class _GitManager {
4653
4926
  detectFrom: options.sourceRoot ? path14.resolve(process.cwd(), options.sourceRoot) : process.cwd()
4654
4927
  });
4655
4928
  const remoteName = options.remoteName ?? "origin";
4656
- let repoExists = false;
4657
- try {
4658
- const checkGit = simpleGit2({ baseDir: options.workspacePath });
4659
- checkGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path14.dirname(options.workspacePath) }));
4660
- await checkGit.raw(["rev-parse", "--git-dir"]);
4661
- repoExists = true;
4662
- } catch {
4929
+ const repoExists = await _GitManager.repoExistsAt(options.workspacePath);
4930
+ if (!repoExists) {
4663
4931
  const gitPath = path14.join(options.workspacePath, ".git");
4664
4932
  try {
4665
4933
  const stat = await fs14.stat(gitPath);
@@ -5611,7 +5879,7 @@ async function generateAIContentFiles(options) {
5611
5879
  }
5612
5880
  const entriesForValidation = await listEntries(branchRoot, flatSchema, contentRootName);
5613
5881
  assertBuildEntriesValid(entriesForValidation, "AI content generation");
5614
- const store = new ContentStore(branchRoot, flatSchema);
5882
+ const store = new ContentStore(branchRoot, flatSchema, { contentRootName });
5615
5883
  const result = await generateAIContent({
5616
5884
  store,
5617
5885
  flatSchema,