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
package/dist/cli/init.js CHANGED
@@ -267,6 +267,16 @@ var init_config = __esm({
267
267
  allowNetworkRemoteInProd: z4.boolean().optional(),
268
268
  settingsBranch: z4.string().optional(),
269
269
  autoCreateSettingsPR: z4.boolean().optional(),
270
+ // Deliberately `deploymentNameSchema.optional()`, NOT `deploymentNameSchema` alone.
271
+ // deploymentNameSchema.default('prod') would make `parse(undefined)` resolve to
272
+ // the literal 'prod' instead of staying `undefined` — collapsing the
273
+ // env > config > modeDefault precedence chain that resolveDeploymentName
274
+ // (operating-mode/deployment-name.ts) implements: config would then always
275
+ // "win" over modeDefault (masking dev's real default of 'local'), and the env
276
+ // var would just be racing config's baked-in 'prod' instead of a true absence.
277
+ // `.optional()` here is what keeps an omitted deploymentName reaching
278
+ // resolveDeploymentName as `undefined`, so it can fall through to modeDefault.
279
+ // Do not "fix" this by removing `.optional()`.
270
280
  deploymentName: deploymentNameSchema.optional(),
271
281
  contentRoot: contentRootSchema.default("content"),
272
282
  sourceRoot: sourceRootSchema.optional(),
@@ -356,14 +366,59 @@ var init_config2 = __esm({
356
366
  }
357
367
  });
358
368
 
369
+ // dist/utils/error.js
370
+ function getErrorMessage(err) {
371
+ if (err instanceof Error) {
372
+ return err.message;
373
+ }
374
+ if (typeof err === "string") {
375
+ return err;
376
+ }
377
+ return String(err);
378
+ }
379
+ function isNodeError(err) {
380
+ return err instanceof Error && "code" in err;
381
+ }
382
+ function isNotFoundError(err) {
383
+ return isNodeError(err) && err.code === "ENOENT";
384
+ }
385
+ var init_error = __esm({
386
+ "dist/utils/error.js"() {
387
+ "use strict";
388
+ }
389
+ });
390
+
391
+ // dist/utils/fs.js
392
+ import fs from "node:fs/promises";
393
+ async function filePathExists(filePath) {
394
+ try {
395
+ await fs.stat(filePath);
396
+ return true;
397
+ } catch (err) {
398
+ if (isNotFoundError(err))
399
+ return false;
400
+ throw err;
401
+ }
402
+ }
403
+ var init_fs = __esm({
404
+ "dist/utils/fs.js"() {
405
+ "use strict";
406
+ init_error();
407
+ }
408
+ });
409
+
359
410
  // dist/cli/templates.js
360
411
  var templates_exports = {};
361
412
  __export(templates_exports, {
413
+ SCAFFOLD_STACK_NAME: () => SCAFFOLD_STACK_NAME,
362
414
  aiConfig: () => aiConfig,
363
415
  aiRoute: () => aiRoute,
364
416
  apiRoute: () => apiRoute,
365
417
  canopyCmsConfig: () => canopyCmsConfig,
366
418
  canopyContext: () => canopyContext,
419
+ cdkApp: () => cdkApp,
420
+ cdkJson: () => cdkJson,
421
+ cmsStack: () => cmsStack,
367
422
  dockerfileCms: () => dockerfileCms,
368
423
  dockerignore: () => dockerignore,
369
424
  editPage: () => editPage,
@@ -427,21 +482,205 @@ async function nextConfig(options) {
427
482
  const templateName = options.staticBuild ? "next.config-static.ts.template" : "next.config.ts.template";
428
483
  return readTemplate(templateName);
429
484
  }
430
- async function dockerfileCms() {
431
- return readTemplate("Dockerfile.cms.template");
485
+ async function dockerfileCms(options) {
486
+ const template = await readTemplate("Dockerfile.cms.template");
487
+ return template.replace("{{DOCKER_COPY}}", options.copy).replace("{{DOCKER_INSTALL}}", options.install).replace("{{DOCKER_BUILD}}", options.build);
432
488
  }
433
489
  async function dockerignore() {
434
490
  return readTemplate("dockerignore.template");
435
491
  }
436
- async function githubWorkflowCms() {
437
- return readTemplate("deploy-cms.yml.template");
492
+ async function githubWorkflowCms(options) {
493
+ const template = await readTemplate("deploy-cms.yml.template");
494
+ return template.replace("{{DEFAULT_BRANCH}}", options.defaultBranch).replace("{{LOCKFILE}}", options.lockfile).replace("{{CI_INSTALL}}", options.ciInstall).replaceAll("{{ADD_DEV}}", options.addDev).replace("{{STACK_NAME}}", SCAFFOLD_STACK_NAME);
495
+ }
496
+ async function cdkJson() {
497
+ return readTemplate("cdk.json.template");
438
498
  }
439
- var __dirname, TEMPLATES_DIR;
499
+ async function cdkApp(options) {
500
+ const template = await readTemplate("cdk-app.ts.template");
501
+ return template.replace("{{STACK_NAME}}", SCAFFOLD_STACK_NAME).replace("{{GITHUB_OWNER}}", options.githubOwner).replace("{{GITHUB_REPO}}", options.githubRepo);
502
+ }
503
+ async function cmsStack() {
504
+ return readTemplate("cms-stack.ts.template");
505
+ }
506
+ var __dirname, TEMPLATES_DIR, SCAFFOLD_STACK_NAME;
440
507
  var init_templates = __esm({
441
508
  "dist/cli/templates.js"() {
442
509
  "use strict";
443
510
  __dirname = path2.dirname(fileURLToPath(import.meta.url));
444
511
  TEMPLATES_DIR = path2.join(__dirname, "template-files");
512
+ SCAFFOLD_STACK_NAME = "CanopyCms";
513
+ }
514
+ });
515
+
516
+ // dist/cli/project-detect.js
517
+ var project_detect_exports = {};
518
+ __export(project_detect_exports, {
519
+ CDK_DEPENDENCIES: () => CDK_DEPENDENCIES,
520
+ commandsFor: () => commandsFor,
521
+ detectDefaultBranch: () => detectDefaultBranch,
522
+ detectGitHubRepo: () => detectGitHubRepo,
523
+ detectPackageManager: () => detectPackageManager,
524
+ missingCdkDependencies: () => missingCdkDependencies,
525
+ readPackageJson: () => readPackageJson
526
+ });
527
+ import fs3 from "node:fs/promises";
528
+ import path3 from "node:path";
529
+ import { simpleGit } from "simple-git";
530
+ function commandsFor(manager) {
531
+ return COMMANDS[manager];
532
+ }
533
+ async function detectPackageManager(projectDir) {
534
+ for (const dir of await workspaceSearchPath(projectDir)) {
535
+ const declared = await readDeclaredPackageManager(dir);
536
+ if (declared)
537
+ return declared;
538
+ if (await filePathExists(path3.join(dir, "pnpm-lock.yaml")))
539
+ return "pnpm";
540
+ if (await filePathExists(path3.join(dir, "yarn.lock"))) {
541
+ return await isYarnBerryLockfile(dir) ? "yarn-berry" : "yarn";
542
+ }
543
+ }
544
+ return "npm";
545
+ }
546
+ async function workspaceSearchPath(projectDir) {
547
+ const start = path3.resolve(projectDir);
548
+ const dirs = [start];
549
+ if (!await filePathExists(path3.join(start, "package.json")))
550
+ return dirs;
551
+ let dir = start;
552
+ for (let depth = 0; depth < MAX_WORKSPACE_DEPTH; depth++) {
553
+ if (await filePathExists(path3.join(dir, ".git")))
554
+ break;
555
+ const parent = path3.dirname(dir);
556
+ if (parent === dir)
557
+ break;
558
+ dir = parent;
559
+ dirs.push(dir);
560
+ }
561
+ return dirs;
562
+ }
563
+ async function readDeclaredPackageManager(projectDir) {
564
+ const pkg = await readPackageJson(projectDir);
565
+ const declared = pkg?.["packageManager"];
566
+ if (typeof declared !== "string")
567
+ return null;
568
+ if (declared.startsWith("pnpm@"))
569
+ return "pnpm";
570
+ if (declared.startsWith("npm@"))
571
+ return "npm";
572
+ if (declared.startsWith("yarn@")) {
573
+ const major = Number.parseInt(declared.slice("yarn@".length), 10);
574
+ if (Number.isFinite(major) && major >= 2)
575
+ return "yarn-berry";
576
+ if (Number.isFinite(major))
577
+ return "yarn";
578
+ return await isYarnBerryLockfile(projectDir) ? "yarn-berry" : "yarn";
579
+ }
580
+ return null;
581
+ }
582
+ async function isYarnBerryLockfile(projectDir) {
583
+ try {
584
+ const content = await fs3.readFile(path3.join(projectDir, "yarn.lock"), "utf-8");
585
+ return content.includes("__metadata:");
586
+ } catch {
587
+ return false;
588
+ }
589
+ }
590
+ async function detectDefaultBranch(projectDir) {
591
+ const head = await gitRaw(projectDir, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]);
592
+ if (!head)
593
+ return "main";
594
+ const slash = head.indexOf("/");
595
+ const branch = slash === -1 ? head : head.slice(slash + 1);
596
+ return branch || "main";
597
+ }
598
+ async function detectGitHubRepo(projectDir) {
599
+ const url = await gitRaw(projectDir, ["remote", "get-url", "origin"]);
600
+ if (!url)
601
+ return null;
602
+ const match = /github\.com[:/]+([^/]+)\/(.+?)(?:\.git)?\/?$/.exec(url);
603
+ if (!match)
604
+ return null;
605
+ return { owner: match[1], repo: match[2] };
606
+ }
607
+ async function gitRaw(projectDir, args) {
608
+ try {
609
+ const output = await simpleGit({ baseDir: projectDir }).raw(args);
610
+ const trimmed = output.trim();
611
+ return trimmed.length > 0 ? trimmed : null;
612
+ } catch {
613
+ return null;
614
+ }
615
+ }
616
+ async function readPackageJson(projectDir) {
617
+ try {
618
+ const raw = await fs3.readFile(path3.join(projectDir, "package.json"), "utf-8");
619
+ const parsed = JSON.parse(raw);
620
+ return typeof parsed === "object" && parsed !== null ? parsed : null;
621
+ } catch {
622
+ return null;
623
+ }
624
+ }
625
+ async function missingCdkDependencies(projectDir) {
626
+ const pkg = await readPackageJson(projectDir);
627
+ const declared = /* @__PURE__ */ new Set();
628
+ for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
629
+ const section = pkg?.[field];
630
+ if (typeof section === "object" && section !== null) {
631
+ for (const name of Object.keys(section))
632
+ declared.add(name);
633
+ }
634
+ }
635
+ return CDK_DEPENDENCIES.filter((name) => !declared.has(name));
636
+ }
637
+ var COMMANDS, MAX_WORKSPACE_DEPTH, CDK_DEPENDENCIES;
638
+ var init_project_detect = __esm({
639
+ "dist/cli/project-detect.js"() {
640
+ "use strict";
641
+ init_fs();
642
+ COMMANDS = {
643
+ npm: {
644
+ lockfile: "package-lock.json",
645
+ ciInstall: "npm ci",
646
+ dockerCopy: "COPY package*.json ./",
647
+ dockerInstall: "RUN npm ci",
648
+ build: "npm run build",
649
+ addDev: "npm install --save-dev"
650
+ },
651
+ pnpm: {
652
+ lockfile: "pnpm-lock.yaml",
653
+ ciInstall: "corepack enable && pnpm install --frozen-lockfile",
654
+ dockerCopy: "COPY package.json pnpm-lock.yaml ./",
655
+ dockerInstall: "RUN corepack enable && pnpm install --frozen-lockfile",
656
+ build: "pnpm run build",
657
+ addDev: "pnpm add -D"
658
+ },
659
+ yarn: {
660
+ lockfile: "yarn.lock",
661
+ ciInstall: "corepack enable && yarn install --frozen-lockfile",
662
+ dockerCopy: "COPY package.json yarn.lock ./",
663
+ dockerInstall: "RUN corepack enable && yarn install --frozen-lockfile",
664
+ build: "yarn build",
665
+ addDev: "yarn add -D"
666
+ },
667
+ "yarn-berry": {
668
+ lockfile: "yarn.lock",
669
+ ciInstall: "corepack enable && yarn install --immutable",
670
+ dockerCopy: "COPY package.json yarn.lock .yarnrc.yml ./",
671
+ dockerInstall: "RUN corepack enable && yarn install --immutable",
672
+ build: "yarn build",
673
+ addDev: "yarn add -D"
674
+ }
675
+ };
676
+ MAX_WORKSPACE_DEPTH = 8;
677
+ CDK_DEPENDENCIES = [
678
+ "canopycms-cdk",
679
+ "aws-cdk-lib",
680
+ "constructs",
681
+ "tsx",
682
+ "aws-cdk"
683
+ ];
445
684
  }
446
685
  });
447
686
 
@@ -450,15 +689,15 @@ var task_queue_config_exports = {};
450
689
  __export(task_queue_config_exports, {
451
690
  getTaskQueueDir: () => getTaskQueueDir
452
691
  });
453
- import path3 from "node:path";
692
+ import path4 from "node:path";
454
693
  function getTaskQueueDir(config) {
455
694
  switch (config.mode) {
456
695
  case "prod": {
457
696
  const workspace = process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE;
458
- return path3.join(path3.resolve(workspace), ".tasks");
697
+ return path4.join(path4.resolve(workspace), ".tasks");
459
698
  }
460
699
  case "dev": {
461
- return path3.join(process.cwd(), ".canopy-dev", ".tasks");
700
+ return path4.join(process.cwd(), ".canopy-dev", ".tasks");
462
701
  }
463
702
  }
464
703
  }
@@ -495,10 +734,21 @@ var init_debug = __esm({
495
734
  const minLevel = this.options.minLevel ?? "DEBUG";
496
735
  return LOG_LEVELS[level] >= LOG_LEVELS[minLevel];
497
736
  }
737
+ /**
738
+ * The ISO-8601 timestamp leads the line BARE - not wrapped in brackets as it
739
+ * once was. `CANOPYCMS_DEBUG=true` can be set on the EC2 worker, whose stdout
740
+ * appends to /var/log/canopy-worker/worker.log; the CloudWatch agent's
741
+ * `multi_line_start_pattern` matches the timestamp at the START of a line
742
+ * (see worker/log.ts's INVARIANT and cms-service.ts's agent config), so a
743
+ * leading `[` meant every debug line was silently folded into the previous
744
+ * event instead of starting its own. The bracketed `[prefix:category]` and
745
+ * `[level]` fields still follow, so the human-readable shape is unchanged
746
+ * apart from those two characters.
747
+ */
498
748
  formatMessage(level, category, message) {
499
749
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
500
750
  const prefix = this.options.prefix ?? "CanopyCMS";
501
- return `[${timestamp}] [${prefix}:${category}] [${level}] ${message}`;
751
+ return `${timestamp} [${prefix}:${category}] [${level}] ${message}`;
502
752
  }
503
753
  debug(category, message, data) {
504
754
  if (this.shouldLog("DEBUG")) {
@@ -567,17 +817,17 @@ var init_debug = __esm({
567
817
  });
568
818
 
569
819
  // dist/utils/atomic-write.js
570
- import fs3 from "node:fs/promises";
571
- import path4 from "node:path";
820
+ import fs4 from "node:fs/promises";
821
+ import path5 from "node:path";
572
822
  async function atomicWriteFile(filePath, content) {
573
- const dir = path4.dirname(filePath);
574
- await fs3.mkdir(dir, { recursive: true });
823
+ const dir = path5.dirname(filePath);
824
+ await fs4.mkdir(dir, { recursive: true });
575
825
  const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
576
- await fs3.writeFile(tempPath, content, typeof content === "string" ? "utf-8" : void 0);
826
+ await fs4.writeFile(tempPath, content, typeof content === "string" ? "utf-8" : void 0);
577
827
  try {
578
- await fs3.rename(tempPath, filePath);
828
+ await fs4.rename(tempPath, filePath);
579
829
  } catch (err) {
580
- await fs3.unlink(tempPath).catch(() => {
830
+ await fs4.unlink(tempPath).catch(() => {
581
831
  });
582
832
  throw err;
583
833
  }
@@ -589,15 +839,15 @@ var init_atomic_write = __esm({
589
839
  });
590
840
 
591
841
  // dist/task-queue/task-queue.js
592
- import fs4 from "node:fs/promises";
593
- import path5 from "node:path";
842
+ import fs5 from "node:fs/promises";
843
+ import path6 from "node:path";
594
844
  import crypto from "node:crypto";
595
845
  function isNotFoundError2(err) {
596
846
  return err instanceof Error && "code" in err && err.code === "ENOENT";
597
847
  }
598
848
  async function enqueueTask(taskDir, task, logger = nullLogger) {
599
849
  const id = crypto.randomUUID();
600
- const pendingDir = path5.join(taskDir, "pending");
850
+ const pendingDir = path6.join(taskDir, "pending");
601
851
  const queuedTask = {
602
852
  id,
603
853
  action: task.action,
@@ -607,17 +857,17 @@ async function enqueueTask(taskDir, task, logger = nullLogger) {
607
857
  retryCount: 0,
608
858
  maxRetries: task.maxRetries ?? DEFAULT_MAX_RETRIES
609
859
  };
610
- const filePath = path5.join(pendingDir, `${id}.json`);
860
+ const filePath = path6.join(pendingDir, `${id}.json`);
611
861
  await atomicWriteFile(filePath, JSON.stringify(queuedTask, null, 2));
612
862
  logger.debug("Enqueued task", { id, action: task.action });
613
863
  return id;
614
864
  }
615
865
  async function dequeueTask(taskDir, logger = nullLogger) {
616
- const pendingDir = path5.join(taskDir, "pending");
617
- const processingDir = path5.join(taskDir, "processing");
866
+ const pendingDir = path6.join(taskDir, "pending");
867
+ const processingDir = path6.join(taskDir, "processing");
618
868
  let files;
619
869
  try {
620
- files = await fs4.readdir(pendingDir);
870
+ files = await fs5.readdir(pendingDir);
621
871
  } catch {
622
872
  return null;
623
873
  }
@@ -628,7 +878,7 @@ async function dequeueTask(taskDir, logger = nullLogger) {
628
878
  const tasks = [];
629
879
  for (const fileName of jsonFiles) {
630
880
  try {
631
- const content = await fs4.readFile(path5.join(pendingDir, fileName), "utf-8");
881
+ const content = await fs5.readFile(path6.join(pendingDir, fileName), "utf-8");
632
882
  const task = parseTaskJson(content);
633
883
  if (!task) {
634
884
  await moveToCorrupt(taskDir, pendingDir, fileName, "Invalid JSON", logger);
@@ -649,17 +899,17 @@ async function dequeueTask(taskDir, logger = nullLogger) {
649
899
  tasks.sort((a, b) => a.task.createdAt.localeCompare(b.task.createdAt) || a.task.id.localeCompare(b.task.id));
650
900
  for (const { fileName, task } of tasks) {
651
901
  if (await taskExistsIn(taskDir, task.id, ["completed", "failed"])) {
652
- await fs4.unlink(path5.join(pendingDir, fileName)).catch(() => {
902
+ await fs5.unlink(path6.join(pendingDir, fileName)).catch(() => {
653
903
  });
654
904
  logger.debug("Skipped already-finished task", { id: task.id });
655
905
  continue;
656
906
  }
657
- const sourcePath = path5.join(pendingDir, fileName);
658
- const destPath = path5.join(processingDir, fileName);
907
+ const sourcePath = path6.join(pendingDir, fileName);
908
+ const destPath = path6.join(processingDir, fileName);
659
909
  try {
660
910
  task.status = "processing";
661
911
  await atomicWriteFile(destPath, JSON.stringify(task, null, 2));
662
- await fs4.unlink(sourcePath);
912
+ await fs5.unlink(sourcePath);
663
913
  logger.debug("Dequeued task", { id: task.id, action: task.action });
664
914
  return task;
665
915
  } catch (err) {
@@ -671,16 +921,16 @@ async function dequeueTask(taskDir, logger = nullLogger) {
671
921
  return null;
672
922
  }
673
923
  async function completeTask(taskDir, taskId, result, logger = nullLogger) {
674
- const processingPath = path5.join(taskDir, "processing", `${taskId}.json`);
675
- const completedDir = path5.join(taskDir, "completed");
676
- const completedPath = path5.join(completedDir, `${taskId}.json`);
924
+ const processingPath = path6.join(taskDir, "processing", `${taskId}.json`);
925
+ const completedDir = path6.join(taskDir, "completed");
926
+ const completedPath = path6.join(completedDir, `${taskId}.json`);
677
927
  let task;
678
928
  try {
679
- const content = await fs4.readFile(processingPath, "utf-8");
929
+ const content = await fs5.readFile(processingPath, "utf-8");
680
930
  const parsed = parseTaskJson(content);
681
931
  if (!parsed) {
682
932
  logger.debug("Corrupt task file in processing, removing", { id: taskId });
683
- await fs4.unlink(processingPath).catch(() => {
933
+ await fs5.unlink(processingPath).catch(() => {
684
934
  });
685
935
  return;
686
936
  }
@@ -694,21 +944,21 @@ async function completeTask(taskDir, taskId, result, logger = nullLogger) {
694
944
  task.completedAt = (/* @__PURE__ */ new Date()).toISOString();
695
945
  task.result = result;
696
946
  await atomicWriteFile(completedPath, JSON.stringify(task, null, 2));
697
- await fs4.unlink(processingPath).catch(() => {
947
+ await fs5.unlink(processingPath).catch(() => {
698
948
  });
699
949
  logger.debug("Completed task", { id: taskId });
700
950
  }
701
951
  async function failTask(taskDir, taskId, error, logger = nullLogger) {
702
- const processingPath = path5.join(taskDir, "processing", `${taskId}.json`);
703
- const failedDir = path5.join(taskDir, "failed");
704
- const failedPath = path5.join(failedDir, `${taskId}.json`);
952
+ const processingPath = path6.join(taskDir, "processing", `${taskId}.json`);
953
+ const failedDir = path6.join(taskDir, "failed");
954
+ const failedPath = path6.join(failedDir, `${taskId}.json`);
705
955
  let task;
706
956
  try {
707
- const content = await fs4.readFile(processingPath, "utf-8");
957
+ const content = await fs5.readFile(processingPath, "utf-8");
708
958
  const parsed = parseTaskJson(content);
709
959
  if (!parsed) {
710
960
  logger.debug("Corrupt task file in processing, removing", { id: taskId });
711
- await fs4.unlink(processingPath).catch(() => {
961
+ await fs5.unlink(processingPath).catch(() => {
712
962
  });
713
963
  return;
714
964
  }
@@ -722,21 +972,21 @@ async function failTask(taskDir, taskId, error, logger = nullLogger) {
722
972
  task.completedAt = (/* @__PURE__ */ new Date()).toISOString();
723
973
  task.error = error;
724
974
  await atomicWriteFile(failedPath, JSON.stringify(task, null, 2));
725
- await fs4.unlink(processingPath).catch(() => {
975
+ await fs5.unlink(processingPath).catch(() => {
726
976
  });
727
977
  logger.debug("Failed task", { id: taskId, error });
728
978
  }
729
979
  async function retryTask(taskDir, taskId, error, logger = nullLogger) {
730
- const processingPath = path5.join(taskDir, "processing", `${taskId}.json`);
731
- const pendingDir = path5.join(taskDir, "pending");
732
- const pendingPath = path5.join(pendingDir, `${taskId}.json`);
980
+ const processingPath = path6.join(taskDir, "processing", `${taskId}.json`);
981
+ const pendingDir = path6.join(taskDir, "pending");
982
+ const pendingPath = path6.join(pendingDir, `${taskId}.json`);
733
983
  let task;
734
984
  try {
735
- const content = await fs4.readFile(processingPath, "utf-8");
985
+ const content = await fs5.readFile(processingPath, "utf-8");
736
986
  const parsed = parseTaskJson(content);
737
987
  if (!parsed) {
738
988
  logger.debug("Corrupt task file in processing, removing", { id: taskId });
739
- await fs4.unlink(processingPath).catch(() => {
989
+ await fs5.unlink(processingPath).catch(() => {
740
990
  });
741
991
  return;
742
992
  }
@@ -753,15 +1003,15 @@ async function retryTask(taskDir, taskId, error, logger = nullLogger) {
753
1003
  task.retryAfter = new Date(Date.now() + backoffMs).toISOString();
754
1004
  task.error = error;
755
1005
  await atomicWriteFile(pendingPath, JSON.stringify(task, null, 2));
756
- await fs4.unlink(processingPath).catch(() => {
1006
+ await fs5.unlink(processingPath).catch(() => {
757
1007
  });
758
1008
  logger.debug("Retrying task", { id: taskId, retryCount, backoffMs });
759
1009
  }
760
1010
  async function requeueFailedTask(taskDir, taskId, logger = nullLogger) {
761
- const failedPath = path5.join(taskDir, "failed", `${taskId}.json`);
1011
+ const failedPath = path6.join(taskDir, "failed", `${taskId}.json`);
762
1012
  let content;
763
1013
  try {
764
- content = await fs4.readFile(failedPath, "utf-8");
1014
+ content = await fs5.readFile(failedPath, "utf-8");
765
1015
  } catch (err) {
766
1016
  if (isNotFoundError2(err))
767
1017
  return { error: "not-found" };
@@ -780,37 +1030,37 @@ async function requeueFailedTask(taskDir, taskId, logger = nullLogger) {
780
1030
  retryCount: 0,
781
1031
  maxRetries: original.maxRetries
782
1032
  };
783
- const pendingPath = path5.join(taskDir, "pending", `${newTaskId}.json`);
1033
+ const pendingPath = path6.join(taskDir, "pending", `${newTaskId}.json`);
784
1034
  await atomicWriteFile(pendingPath, JSON.stringify(newTask, null, 2));
785
- await fs4.unlink(failedPath).catch(() => {
1035
+ await fs5.unlink(failedPath).catch(() => {
786
1036
  });
787
1037
  logger.debug("Requeued failed task", { originalId: taskId, newTaskId });
788
1038
  return { newTaskId };
789
1039
  }
790
1040
  async function recoverOrphanedTasks(taskDir, maxAgeMs = 5 * 6e4, logger = nullLogger) {
791
- const processingDir = path5.join(taskDir, "processing");
792
- const pendingDir = path5.join(taskDir, "pending");
1041
+ const processingDir = path6.join(taskDir, "processing");
1042
+ const pendingDir = path6.join(taskDir, "pending");
793
1043
  let files;
794
1044
  try {
795
- files = await fs4.readdir(processingDir);
1045
+ files = await fs5.readdir(processingDir);
796
1046
  } catch {
797
1047
  return 0;
798
1048
  }
799
1049
  const now = Date.now();
800
1050
  let recovered = 0;
801
1051
  for (const fileName of files.filter((f) => f.endsWith(".json"))) {
802
- const filePath = path5.join(processingDir, fileName);
1052
+ const filePath = path6.join(processingDir, fileName);
803
1053
  try {
804
- const stat = await fs4.stat(filePath);
1054
+ const stat = await fs5.stat(filePath);
805
1055
  if (now - stat.mtimeMs >= maxAgeMs) {
806
- const content = await fs4.readFile(filePath, "utf-8");
1056
+ const content = await fs5.readFile(filePath, "utf-8");
807
1057
  const task = parseTaskJson(content);
808
1058
  if (!task) {
809
1059
  await moveToCorrupt(taskDir, processingDir, fileName, "Invalid JSON during recovery", logger);
810
1060
  continue;
811
1061
  }
812
1062
  if (await taskExistsIn(taskDir, task.id, ["completed", "failed"])) {
813
- await fs4.unlink(filePath).catch(() => {
1063
+ await fs5.unlink(filePath).catch(() => {
814
1064
  });
815
1065
  logger.debug("Cleaned up orphaned task (already finished)", {
816
1066
  id: task.id
@@ -818,8 +1068,8 @@ async function recoverOrphanedTasks(taskDir, maxAgeMs = 5 * 6e4, logger = nullLo
818
1068
  continue;
819
1069
  }
820
1070
  task.status = "pending";
821
- await atomicWriteFile(path5.join(pendingDir, fileName), JSON.stringify(task, null, 2));
822
- await fs4.unlink(filePath);
1071
+ await atomicWriteFile(path6.join(pendingDir, fileName), JSON.stringify(task, null, 2));
1072
+ await fs5.unlink(filePath);
823
1073
  logger.debug("Recovered orphaned task", {
824
1074
  id: task.id,
825
1075
  action: task.action
@@ -838,19 +1088,19 @@ async function cleanupOldTasks(taskDir, maxAgeMs = 30 * 24 * 60 * 6e4, logger =
838
1088
  const now = Date.now();
839
1089
  let cleaned = 0;
840
1090
  for (const subdir of ["completed", "failed"]) {
841
- const dir = path5.join(taskDir, subdir);
1091
+ const dir = path6.join(taskDir, subdir);
842
1092
  let files;
843
1093
  try {
844
- files = await fs4.readdir(dir);
1094
+ files = await fs5.readdir(dir);
845
1095
  } catch {
846
1096
  continue;
847
1097
  }
848
1098
  for (const fileName of files.filter((f) => f.endsWith(".json"))) {
849
1099
  try {
850
- const filePath = path5.join(dir, fileName);
851
- const stat = await fs4.stat(filePath);
1100
+ const filePath = path6.join(dir, fileName);
1101
+ const stat = await fs5.stat(filePath);
852
1102
  if (now - stat.mtimeMs >= maxAgeMs) {
853
- await fs4.unlink(filePath);
1103
+ await fs5.unlink(filePath);
854
1104
  cleaned++;
855
1105
  }
856
1106
  } catch {
@@ -864,9 +1114,9 @@ async function cleanupOldTasks(taskDir, maxAgeMs = 30 * 24 * 60 * 6e4, logger =
864
1114
  }
865
1115
  async function getTask(taskDir, taskId) {
866
1116
  for (const subdir of ["completed", "failed", "processing", "pending"]) {
867
- const filePath = path5.join(taskDir, subdir, `${taskId}.json`);
1117
+ const filePath = path6.join(taskDir, subdir, `${taskId}.json`);
868
1118
  try {
869
- const content = await fs4.readFile(filePath, "utf-8");
1119
+ const content = await fs5.readFile(filePath, "utf-8");
870
1120
  return parseTaskJson(content);
871
1121
  } catch {
872
1122
  continue;
@@ -875,10 +1125,10 @@ async function getTask(taskDir, taskId) {
875
1125
  return null;
876
1126
  }
877
1127
  async function listTasks(taskDir, status, limit = 100) {
878
- const dir = path5.join(taskDir, status);
1128
+ const dir = path6.join(taskDir, status);
879
1129
  let files;
880
1130
  try {
881
- files = await fs4.readdir(dir);
1131
+ files = await fs5.readdir(dir);
882
1132
  } catch {
883
1133
  return [];
884
1134
  }
@@ -887,7 +1137,7 @@ async function listTasks(taskDir, status, limit = 100) {
887
1137
  if (tasks.length >= limit)
888
1138
  break;
889
1139
  try {
890
- const content = await fs4.readFile(path5.join(dir, fileName), "utf-8");
1140
+ const content = await fs5.readFile(path6.join(dir, fileName), "utf-8");
891
1141
  const task = parseTaskJson(content);
892
1142
  if (task)
893
1143
  tasks.push(task);
@@ -907,9 +1157,9 @@ async function getQueueStats(taskDir) {
907
1157
  corrupt: 0
908
1158
  };
909
1159
  for (const status of ["pending", "processing", "completed", "failed", "corrupt"]) {
910
- const dir = path5.join(taskDir, status);
1160
+ const dir = path6.join(taskDir, status);
911
1161
  try {
912
- const files = await fs4.readdir(dir);
1162
+ const files = await fs5.readdir(dir);
913
1163
  stats[status] = files.filter((f) => f.endsWith(".json")).length;
914
1164
  } catch {
915
1165
  }
@@ -917,10 +1167,10 @@ async function getQueueStats(taskDir) {
917
1167
  return stats;
918
1168
  }
919
1169
  async function listCorruptTaskFiles(taskDir, limit = 50, logger = nullLogger) {
920
- const corruptDir = path5.join(taskDir, "corrupt");
1170
+ const corruptDir = path6.join(taskDir, "corrupt");
921
1171
  let files;
922
1172
  try {
923
- files = await fs4.readdir(corruptDir);
1173
+ files = await fs5.readdir(corruptDir);
924
1174
  } catch (err) {
925
1175
  if (isNotFoundError2(err))
926
1176
  return [];
@@ -928,9 +1178,9 @@ async function listCorruptTaskFiles(taskDir, limit = 50, logger = nullLogger) {
928
1178
  }
929
1179
  const entries = [];
930
1180
  for (const fileName of files.filter((f) => f.endsWith(".json"))) {
931
- const filePath = path5.join(corruptDir, fileName);
1181
+ const filePath = path6.join(corruptDir, fileName);
932
1182
  try {
933
- const stat = await fs4.stat(filePath);
1183
+ const stat = await fs5.stat(filePath);
934
1184
  entries.push({
935
1185
  fileName,
936
1186
  size: stat.size,
@@ -947,7 +1197,7 @@ async function listCorruptTaskFiles(taskDir, limit = 50, logger = nullLogger) {
947
1197
  return entries.slice(0, limit);
948
1198
  }
949
1199
  async function readSnippet(filePath, maxBytes) {
950
- const handle = await fs4.open(filePath, "r");
1200
+ const handle = await fs5.open(filePath, "r");
951
1201
  try {
952
1202
  const buffer = Buffer.alloc(maxBytes);
953
1203
  const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
@@ -970,7 +1220,7 @@ function parseTaskJson(content) {
970
1220
  async function taskExistsIn(taskDir, taskId, subdirs) {
971
1221
  for (const subdir of subdirs) {
972
1222
  try {
973
- await fs4.stat(path5.join(taskDir, subdir, `${taskId}.json`));
1223
+ await fs5.stat(path6.join(taskDir, subdir, `${taskId}.json`));
974
1224
  return true;
975
1225
  } catch {
976
1226
  }
@@ -978,13 +1228,13 @@ async function taskExistsIn(taskDir, taskId, subdirs) {
978
1228
  return false;
979
1229
  }
980
1230
  async function moveToCorrupt(taskDir, sourceDir, fileName, reason, logger) {
981
- const corruptDir = path5.join(taskDir, "corrupt");
1231
+ const corruptDir = path6.join(taskDir, "corrupt");
982
1232
  try {
983
- await fs4.mkdir(corruptDir, { recursive: true });
984
- await fs4.rename(path5.join(sourceDir, fileName), path5.join(corruptDir, fileName));
1233
+ await fs5.mkdir(corruptDir, { recursive: true });
1234
+ await fs5.rename(path6.join(sourceDir, fileName), path6.join(corruptDir, fileName));
985
1235
  logger.debug("Moved corrupt task file", { fileName, reason });
986
1236
  } catch {
987
- await fs4.unlink(path5.join(sourceDir, fileName)).catch(() => {
1237
+ await fs5.unlink(path6.join(sourceDir, fileName)).catch(() => {
988
1238
  });
989
1239
  }
990
1240
  }
@@ -1041,8 +1291,8 @@ var init_task_queue3 = __esm({
1041
1291
  });
1042
1292
 
1043
1293
  // dist/cli/init.js
1044
- import fs5 from "node:fs/promises";
1045
- import path6 from "node:path";
1294
+ import fs6 from "node:fs/promises";
1295
+ import path7 from "node:path";
1046
1296
  import * as p from "@clack/prompts";
1047
1297
  import { createJiti } from "jiti";
1048
1298
 
@@ -1112,6 +1362,33 @@ var DevClientSafeStrategy = class {
1112
1362
 
1113
1363
  // dist/operating-mode/client-unsafe-strategy.js
1114
1364
  import path from "node:path";
1365
+
1366
+ // dist/utils/logger.js
1367
+ var active = console;
1368
+ function canopyLogWarn(...args) {
1369
+ active.warn(...args);
1370
+ }
1371
+
1372
+ // dist/operating-mode/deployment-name.js
1373
+ var warned = false;
1374
+ var VALID_DEPLOYMENT_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
1375
+ var isValidDeploymentName = (name) => VALID_DEPLOYMENT_NAME.test(name) && !name.includes("..") && !name.endsWith(".") && !name.endsWith(".lock");
1376
+ function resolveDeploymentName(config, modeDefault) {
1377
+ const envValue = process.env.CANOPYCMS_DEPLOYMENT_NAME?.trim();
1378
+ const configValue = config.deploymentName;
1379
+ if (envValue && configValue && envValue !== configValue && !warned) {
1380
+ 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.`);
1381
+ warned = true;
1382
+ }
1383
+ const resolved = envValue || configValue || modeDefault;
1384
+ if (!isValidDeploymentName(resolved)) {
1385
+ const source = envValue ? "CANOPYCMS_DEPLOYMENT_NAME" : configValue ? "config.deploymentName" : "the mode default";
1386
+ 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'.`);
1387
+ }
1388
+ return resolved;
1389
+ }
1390
+
1391
+ // dist/operating-mode/client-unsafe-strategy.js
1115
1392
  init_config2();
1116
1393
  var ProdStrategy = class extends ProdClientSafeStrategy {
1117
1394
  // All client-safe methods inherited automatically from ProdClientSafeStrategy:
@@ -1122,8 +1399,8 @@ var ProdStrategy = class extends ProdClientSafeStrategy {
1122
1399
  getWorkspaceRoot(_sourceRoot) {
1123
1400
  return path.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE);
1124
1401
  }
1125
- getContentRoot(sourceRoot) {
1126
- return path.resolve(sourceRoot ?? process.cwd(), "content");
1402
+ getContentRoot(contentRoot, sourceRoot) {
1403
+ return path.resolve(sourceRoot ?? process.cwd(), contentRoot);
1127
1404
  }
1128
1405
  getContentBranchesRoot(sourceRoot) {
1129
1406
  return path.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
@@ -1154,8 +1431,7 @@ var ProdStrategy = class extends ProdClientSafeStrategy {
1154
1431
  getSettingsBranchName(config) {
1155
1432
  if (config.settingsBranch)
1156
1433
  return config.settingsBranch;
1157
- const deploymentName = config.deploymentName ?? "prod";
1158
- return `canopycms-settings-${deploymentName}`;
1434
+ return `canopycms-settings-${resolveDeploymentName(config, "prod")}`;
1159
1435
  }
1160
1436
  getSettingsRoot(sourceRoot) {
1161
1437
  return path.join(this.getWorkspaceRoot(sourceRoot), "settings");
@@ -1177,8 +1453,8 @@ var DevStrategy = class extends DevClientSafeStrategy {
1177
1453
  getWorkspaceRoot(sourceRoot) {
1178
1454
  return path.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
1179
1455
  }
1180
- getContentRoot(sourceRoot) {
1181
- return path.resolve(sourceRoot ?? process.cwd(), "content");
1456
+ getContentRoot(contentRoot, sourceRoot) {
1457
+ return path.resolve(sourceRoot ?? process.cwd(), contentRoot);
1182
1458
  }
1183
1459
  getContentBranchesRoot(sourceRoot) {
1184
1460
  return path.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
@@ -1208,8 +1484,7 @@ var DevStrategy = class extends DevClientSafeStrategy {
1208
1484
  getSettingsBranchName(config) {
1209
1485
  if (config.settingsBranch)
1210
1486
  return config.settingsBranch;
1211
- const deploymentName = config.deploymentName ?? "local";
1212
- return `canopycms-settings-${deploymentName}`;
1487
+ return `canopycms-settings-${resolveDeploymentName(config, "local")}`;
1213
1488
  }
1214
1489
  getSettingsRoot(sourceRoot) {
1215
1490
  return path.join(this.getWorkspaceRoot(sourceRoot), "settings");
@@ -1252,41 +1527,11 @@ function assertAuthPluginAllowedForMode(plugin, mode) {
1252
1527
  }
1253
1528
  }
1254
1529
 
1255
- // dist/utils/fs.js
1256
- import fs from "node:fs/promises";
1257
-
1258
- // dist/utils/error.js
1259
- function getErrorMessage(err) {
1260
- if (err instanceof Error) {
1261
- return err.message;
1262
- }
1263
- if (typeof err === "string") {
1264
- return err;
1265
- }
1266
- return String(err);
1267
- }
1268
- function isNodeError(err) {
1269
- return err instanceof Error && "code" in err;
1270
- }
1271
- function isNotFoundError(err) {
1272
- return isNodeError(err) && err.code === "ENOENT";
1273
- }
1274
-
1275
- // dist/utils/fs.js
1276
- async function filePathExists(filePath) {
1277
- try {
1278
- await fs.stat(filePath);
1279
- return true;
1280
- } catch (err) {
1281
- if (isNotFoundError(err))
1282
- return false;
1283
- throw err;
1284
- }
1285
- }
1286
-
1287
1530
  // dist/cli/init.js
1531
+ init_fs();
1532
+ init_error();
1288
1533
  async function writeFile(filePath, content, options) {
1289
- const relativePath = path6.relative(process.cwd(), filePath);
1534
+ const relativePath = path7.relative(process.cwd(), filePath);
1290
1535
  if (await filePathExists(filePath)) {
1291
1536
  if (options.force) {
1292
1537
  } else if (options.nonInteractive) {
@@ -1303,8 +1548,8 @@ async function writeFile(filePath, content, options) {
1303
1548
  }
1304
1549
  }
1305
1550
  }
1306
- await fs5.mkdir(path6.dirname(filePath), { recursive: true });
1307
- await fs5.writeFile(filePath, content, "utf-8");
1551
+ await fs6.mkdir(path7.dirname(filePath), { recursive: true });
1552
+ await fs6.writeFile(filePath, content, "utf-8");
1308
1553
  p.log.success(`created: ${relativePath}`);
1309
1554
  return true;
1310
1555
  }
@@ -1356,24 +1601,24 @@ async function init(options) {
1356
1601
  }
1357
1602
  const serverPageExt = staticBuild ? "page.server.tsx" : "page.tsx";
1358
1603
  const serverRouteExt = staticBuild ? "route.server.ts" : "route.ts";
1359
- await writeFile(path6.join(projectDir, "canopycms.config.ts"), await canopyCmsConfig2({ mode, staticBuild }), writeOpts);
1360
- await writeFile(path6.join(projectDir, appDir, "lib/canopy.ts"), await canopyContext2({ configImport: configImportPath(appDir, 1), authProvider }), writeOpts);
1361
- await writeFile(path6.join(projectDir, appDir, "schemas.ts"), await schemasTemplate2(), writeOpts);
1362
- await writeFile(path6.join(projectDir, appDir, `api/canopycms/[...canopycms]/${serverRouteExt}`), await apiRoute2({
1604
+ await writeFile(path7.join(projectDir, "canopycms.config.ts"), await canopyCmsConfig2({ mode, staticBuild }), writeOpts);
1605
+ await writeFile(path7.join(projectDir, appDir, "lib/canopy.ts"), await canopyContext2({ configImport: configImportPath(appDir, 1), authProvider }), writeOpts);
1606
+ await writeFile(path7.join(projectDir, appDir, "schemas.ts"), await schemasTemplate2(), writeOpts);
1607
+ await writeFile(path7.join(projectDir, appDir, `api/canopycms/[...canopycms]/${serverRouteExt}`), await apiRoute2({
1363
1608
  canopyImport: "../".repeat(3) + "lib/canopy"
1364
1609
  }), writeOpts);
1365
- await writeFile(path6.join(projectDir, appDir, `edit/${serverPageExt}`), await editPage2({ configImport: configImportPath(appDir, 1), authProvider }), writeOpts);
1610
+ await writeFile(path7.join(projectDir, appDir, `edit/${serverPageExt}`), await editPage2({ configImport: configImportPath(appDir, 1), authProvider }), writeOpts);
1366
1611
  if (ai) {
1367
- await writeFile(path6.join(projectDir, appDir, "ai/config.ts"), await aiConfig2(), writeOpts);
1368
- await writeFile(path6.join(projectDir, appDir, `ai/[...path]/${serverRouteExt}`), await aiRoute2({ configImport: configImportPath(appDir, 2) }), writeOpts);
1612
+ await writeFile(path7.join(projectDir, appDir, "ai/config.ts"), await aiConfig2(), writeOpts);
1613
+ await writeFile(path7.join(projectDir, appDir, `ai/[...path]/${serverRouteExt}`), await aiRoute2({ configImport: configImportPath(appDir, 2) }), writeOpts);
1369
1614
  }
1370
- await writeFile(path6.join(projectDir, "next.config.ts"), await nextConfig2({ staticBuild }), writeOpts);
1371
- await writeFile(path6.join(projectDir, "middleware.ts"), await middleware2({ authProvider }), writeOpts);
1372
- const gitignorePath = path6.join(projectDir, ".gitignore");
1615
+ await writeFile(path7.join(projectDir, "next.config.ts"), await nextConfig2({ staticBuild }), writeOpts);
1616
+ await writeFile(path7.join(projectDir, "middleware.ts"), await middleware2({ authProvider }), writeOpts);
1617
+ const gitignorePath = path7.join(projectDir, ".gitignore");
1373
1618
  if (await filePathExists(gitignorePath)) {
1374
- const content = await fs5.readFile(gitignorePath, "utf-8");
1619
+ const content = await fs6.readFile(gitignorePath, "utf-8");
1375
1620
  if (!content.includes(".canopy-dev")) {
1376
- await fs5.appendFile(gitignorePath, "\n# CanopyCMS\n.canopy-dev/\n");
1621
+ await fs6.appendFile(gitignorePath, "\n# CanopyCMS\n.canopy-dev/\n");
1377
1622
  p.log.success("updated: .gitignore");
1378
1623
  }
1379
1624
  }
@@ -1398,34 +1643,78 @@ async function init(options) {
1398
1643
  async function initDeployAws(options) {
1399
1644
  const { projectDir, force, nonInteractive } = options;
1400
1645
  const writeOpts = { force, nonInteractive };
1401
- const { dockerfileCms: dockerfileCms2, dockerignore: dockerignore2, githubWorkflowCms: githubWorkflowCms2 } = await Promise.resolve().then(() => (init_templates(), templates_exports));
1646
+ const { dockerfileCms: dockerfileCms2, dockerignore: dockerignore2, githubWorkflowCms: githubWorkflowCms2, cdkJson: cdkJson2, cdkApp: cdkApp2, cmsStack: cmsStack2 } = await Promise.resolve().then(() => (init_templates(), templates_exports));
1647
+ const { detectPackageManager: detectPackageManager2, detectDefaultBranch: detectDefaultBranch2, detectGitHubRepo: detectGitHubRepo2, commandsFor: commandsFor2, missingCdkDependencies: missingCdkDependencies2, CDK_DEPENDENCIES: CDK_DEPENDENCIES2 } = await Promise.resolve().then(() => (init_project_detect(), project_detect_exports));
1402
1648
  p.intro("CanopyCMS init-deploy aws");
1403
- await writeFile(path6.join(projectDir, "Dockerfile.cms"), await dockerfileCms2(), writeOpts);
1404
- const dockerignoreWritten = await writeFile(path6.join(projectDir, ".dockerignore"), await dockerignore2(), writeOpts);
1649
+ const packageManager = await detectPackageManager2(projectDir);
1650
+ const pm = commandsFor2(packageManager);
1651
+ const defaultBranch = await detectDefaultBranch2(projectDir);
1652
+ const repo = await detectGitHubRepo2(projectDir);
1653
+ await writeFile(path7.join(projectDir, "Dockerfile.cms"), await dockerfileCms2({ copy: pm.dockerCopy, install: pm.dockerInstall, build: pm.build }), writeOpts);
1654
+ const dockerignoreWritten = await writeFile(path7.join(projectDir, ".dockerignore"), await dockerignore2(), writeOpts);
1405
1655
  if (!dockerignoreWritten) {
1406
- p.log.warn("Existing .dockerignore kept \u2014 verify it excludes .env* (secrets would otherwise enter the build context) and does NOT exclude vendor/ (needed for `npm ci` with file: deps).");
1656
+ p.log.warn("Existing .dockerignore kept \u2014 verify it excludes .env* (secrets would otherwise enter the build context) and does NOT exclude vendor/ (needed by the Dockerfile.cms install step with file: deps).");
1657
+ }
1658
+ await writeFile(path7.join(projectDir, ".github/workflows/deploy-cms.yml"), await githubWorkflowCms2({
1659
+ defaultBranch,
1660
+ lockfile: pm.lockfile,
1661
+ ciInstall: pm.ciInstall,
1662
+ addDev: pm.addDev
1663
+ }), writeOpts);
1664
+ const cdkJsonPath = path7.join(projectDir, "cdk.json");
1665
+ const existingCdkJson = await filePathExists(cdkJsonPath) ? await fs6.readFile(cdkJsonPath, "utf-8") : null;
1666
+ const cdkJsonWritten = await writeFile(cdkJsonPath, await cdkJson2(), writeOpts);
1667
+ await writeFile(path7.join(projectDir, "infrastructure/bin/app.ts"), await cdkApp2({
1668
+ githubOwner: repo?.owner ?? "your-org",
1669
+ githubRepo: repo?.repo ?? "your-docs-site"
1670
+ }), writeOpts);
1671
+ await writeFile(path7.join(projectDir, "infrastructure/lib/cms-stack.ts"), await cmsStack2(), writeOpts);
1672
+ if (!cdkJsonWritten && existingCdkJson && !existingCdkJson.includes("infrastructure/bin/app.ts")) {
1673
+ p.log.warn("Existing cdk.json kept, and it does not point at infrastructure/bin/app.ts \u2014 the scaffolded CDK app will not be deployed. Either wire CmsStack into your own app entry point, or re-run with --force to replace cdk.json.");
1407
1674
  }
1408
- await writeFile(path6.join(projectDir, ".github/workflows/deploy-cms.yml"), await githubWorkflowCms2(), writeOpts);
1409
- const nextConfigPath = path6.join(projectDir, "next.config.ts");
1410
- const nextConfigMjsPath = path6.join(projectDir, "next.config.mjs");
1675
+ const nextConfigPath = path7.join(projectDir, "next.config.ts");
1676
+ const nextConfigMjsPath = path7.join(projectDir, "next.config.mjs");
1411
1677
  const configPath = await filePathExists(nextConfigPath) ? nextConfigPath : await filePathExists(nextConfigMjsPath) ? nextConfigMjsPath : null;
1412
1678
  if (configPath) {
1413
- const content = await fs5.readFile(configPath, "utf-8");
1679
+ const content = await fs6.readFile(configPath, "utf-8");
1414
1680
  if (!content.includes("CANOPY_BUILD")) {
1415
1681
  p.note([
1416
- `Add dual build support to ${path6.basename(configPath)}:`,
1682
+ `Add dual build support to ${path7.basename(configPath)}:`,
1417
1683
  "",
1418
1684
  " output: process.env.CANOPY_BUILD === 'cms' ? 'standalone' : 'export',"
1419
1685
  ].join("\n"), "Manual step");
1420
1686
  }
1421
1687
  }
1422
- p.note("CDK constructs are available via the canopycms-cdk package.\nSee the deployment plan for CDK stack setup.", "AWS deployment");
1688
+ if (packageManager === "yarn" || packageManager === "yarn-berry") {
1689
+ p.log.warn(`Yarn detected (${packageManager === "yarn-berry" ? "Berry" : "classic"}). The generated install lines are a best effort and are not exercised by CanopyCMS tests \u2014 review them in Dockerfile.cms and .github/workflows/deploy-cms.yml.` + (packageManager === "yarn-berry" ? " Berry additionally requires nodeLinker: node-modules; PnP is incompatible with Next.js standalone output, which the CMS image's runner stage copies." : ""));
1690
+ }
1691
+ const missing = await missingCdkDependencies2(projectDir);
1692
+ if (missing.length > 0) {
1693
+ p.log.warn(`Not installed: ${missing.join(", ")}. cdk.json runs \`node --import tsx infrastructure/bin/app.ts\`, so the deploy fails without them:
1694
+ ${pm.addDev} ${CDK_DEPENDENCIES2.join(" ")}`);
1695
+ }
1696
+ p.note([
1697
+ "1. Install the CDK dependencies (if you have not already):",
1698
+ ` ${pm.addDev} ${CDK_DEPENDENCIES2.join(" ")}`,
1699
+ "",
1700
+ "2. Fill in infrastructure/lib/cms-stack.ts and infrastructure/bin/app.ts",
1701
+ ` (worker repo currently set to ${repo ? `${repo.owner}/${repo.repo}` : "your-org/your-docs-site"})`,
1702
+ "",
1703
+ "3. Bootstrap CDK in the target account/region: cdk bootstrap",
1704
+ "",
1705
+ "4. Set the repository secrets and variables listed at the top of",
1706
+ " .github/workflows/deploy-cms.yml \u2014 the deploy fails at synth without them.",
1707
+ "",
1708
+ `Deploys trigger on pushes to '${defaultBranch}'; edit the workflow to change that.`,
1709
+ "",
1710
+ "Full walkthrough: docs/deploying-to-aws.md"
1711
+ ].join("\n"), "AWS deployment");
1423
1712
  p.outro("Done!");
1424
1713
  }
1425
1714
  async function detectMode(projectDir) {
1426
- const cfgPath = path6.join(projectDir, "canopycms.config.ts");
1715
+ const cfgPath = path7.join(projectDir, "canopycms.config.ts");
1427
1716
  try {
1428
- await fs5.stat(cfgPath);
1717
+ await fs6.stat(cfgPath);
1429
1718
  } catch (err) {
1430
1719
  if (isNotFoundError(err))
1431
1720
  return "dev";
@@ -1456,7 +1745,7 @@ async function workerRunOnce(options) {
1456
1745
  assertAuthPluginAllowedForMode(options.authPlugin, mode);
1457
1746
  }
1458
1747
  const taskDir = getTaskQueueDir2({ mode });
1459
- const cachePath = process.env.CANOPY_AUTH_CACHE_PATH ?? path6.join(operatingStrategy(mode).getWorkspaceRoot(options.projectDir), ".cache");
1748
+ const cachePath = process.env.CANOPY_AUTH_CACHE_PATH ?? path7.join(operatingStrategy(mode).getWorkspaceRoot(options.projectDir), ".cache");
1460
1749
  let refreshAuthCache;
1461
1750
  const authMode = process.env.CANOPY_AUTH_MODE || "dev";
1462
1751
  if (options.authPlugin?.createCacheRefresher) {