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.
- package/dist/ai/handler.js +1 -1
- package/dist/ai/handler.js.map +1 -1
- package/dist/api/assets.d.ts.map +1 -1
- package/dist/api/assets.js +36 -4
- package/dist/api/assets.js.map +1 -1
- package/dist/api/branch-status.d.ts.map +1 -1
- package/dist/api/branch-status.js +56 -0
- package/dist/api/branch-status.js.map +1 -1
- package/dist/api/branch-withdraw.d.ts.map +1 -1
- package/dist/api/branch-withdraw.js +14 -3
- package/dist/api/branch-withdraw.js.map +1 -1
- package/dist/api/branch.d.ts +31 -2
- package/dist/api/branch.d.ts.map +1 -1
- package/dist/api/branch.js +252 -13
- package/dist/api/branch.js.map +1 -1
- package/dist/api/content.d.ts.map +1 -1
- package/dist/api/content.js +12 -4
- package/dist/api/content.js.map +1 -1
- package/dist/api/entries.d.ts.map +1 -1
- package/dist/api/entries.js +11 -9
- package/dist/api/entries.js.map +1 -1
- package/dist/api/guards.d.ts.map +1 -1
- package/dist/api/guards.js +12 -5
- package/dist/api/guards.js.map +1 -1
- package/dist/api/permissions.d.ts +2 -2
- package/dist/api/permissions.d.ts.map +1 -1
- package/dist/api/permissions.js +51 -3
- package/dist/api/permissions.js.map +1 -1
- package/dist/api/reference-options.d.ts.map +1 -1
- package/dist/api/reference-options.js +10 -17
- package/dist/api/reference-options.js.map +1 -1
- package/dist/api/resolve-references.d.ts.map +1 -1
- package/dist/api/resolve-references.js +3 -1
- package/dist/api/resolve-references.js.map +1 -1
- package/dist/api/schema.d.ts.map +1 -1
- package/dist/api/schema.js +3 -2
- package/dist/api/schema.js.map +1 -1
- package/dist/api/settings-helpers.d.ts.map +1 -1
- package/dist/api/settings-helpers.js +6 -5
- package/dist/api/settings-helpers.js.map +1 -1
- package/dist/assets/pipeline.d.ts +11 -2
- package/dist/assets/pipeline.d.ts.map +1 -1
- package/dist/assets/pipeline.js +114 -2
- package/dist/assets/pipeline.js.map +1 -1
- package/dist/assets/transform-directives.d.ts +18 -0
- package/dist/assets/transform-directives.d.ts.map +1 -1
- package/dist/assets/transform-directives.js +18 -0
- package/dist/assets/transform-directives.js.map +1 -1
- package/dist/assets/transform.d.ts.map +1 -1
- package/dist/assets/transform.js +1 -10
- package/dist/assets/transform.js.map +1 -1
- package/dist/auth/types.d.ts +29 -0
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/authorization/index.d.ts +1 -1
- package/dist/authorization/index.d.ts.map +1 -1
- package/dist/authorization/index.js +1 -1
- package/dist/authorization/index.js.map +1 -1
- package/dist/authorization/protected-branch.d.ts +75 -0
- package/dist/authorization/protected-branch.d.ts.map +1 -1
- package/dist/authorization/protected-branch.js +39 -0
- package/dist/authorization/protected-branch.js.map +1 -1
- package/dist/branch-registry.d.ts.map +1 -1
- package/dist/branch-registry.js +5 -1
- package/dist/branch-registry.js.map +1 -1
- package/dist/branch-schema-cache.d.ts.map +1 -1
- package/dist/branch-schema-cache.js +9 -0
- package/dist/branch-schema-cache.js.map +1 -1
- package/dist/build/generate-ai-content.js +1 -1
- package/dist/build/generate-ai-content.js.map +1 -1
- package/dist/cli/cli.js +1442 -828
- package/dist/cli/generate-ai-content.js +404 -136
- package/dist/cli/init.d.ts +8 -2
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +440 -151
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/project-detect.d.ts +89 -0
- package/dist/cli/project-detect.d.ts.map +1 -0
- package/dist/cli/project-detect.js +240 -0
- package/dist/cli/project-detect.js.map +1 -0
- package/dist/cli/template-files/Dockerfile.cms.template +11 -6
- package/dist/cli/template-files/cdk-app.ts.template +82 -0
- package/dist/cli/template-files/cdk.json.template +9 -0
- package/dist/cli/template-files/cms-stack.ts.template +124 -0
- package/dist/cli/template-files/deploy-cms.yml.template +125 -13
- package/dist/cli/template-files/dockerignore.template +2 -1
- package/dist/cli/templates.d.ts +26 -2
- package/dist/cli/templates.d.ts.map +1 -1
- package/dist/cli/templates.js +36 -4
- package/dist/cli/templates.js.map +1 -1
- package/dist/config/schemas/config.d.ts +2 -3
- package/dist/config/schemas/config.d.ts.map +1 -1
- package/dist/config/schemas/config.js +21 -1
- package/dist/config/schemas/config.js.map +1 -1
- package/dist/config/validation.d.ts +9 -0
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +22 -12
- package/dist/config/validation.js.map +1 -1
- package/dist/content-reader.js +1 -1
- package/dist/content-reader.js.map +1 -1
- package/dist/content-store.d.ts +11 -0
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +2 -1
- package/dist/content-store.js.map +1 -1
- package/dist/dev-content-watcher.d.ts.map +1 -1
- package/dist/dev-content-watcher.js +4 -1
- package/dist/dev-content-watcher.js.map +1 -1
- package/dist/editor/BranchManager.d.ts +15 -0
- package/dist/editor/BranchManager.d.ts.map +1 -1
- package/dist/editor/BranchManager.js +43 -9
- package/dist/editor/BranchManager.js.map +1 -1
- package/dist/editor/CanopyEditor.d.ts.map +1 -1
- package/dist/editor/CanopyEditor.js +2 -2
- package/dist/editor/CanopyEditor.js.map +1 -1
- package/dist/editor/Editor.d.ts.map +1 -1
- package/dist/editor/Editor.js +110 -48
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/admin/SystemHealthPanel.d.ts.map +1 -1
- package/dist/editor/admin/SystemHealthPanel.js +9 -8
- package/dist/editor/admin/SystemHealthPanel.js.map +1 -1
- package/dist/editor/components/EditorHeader.d.ts +6 -0
- package/dist/editor/components/EditorHeader.d.ts.map +1 -1
- package/dist/editor/components/EditorHeader.js +76 -23
- package/dist/editor/components/EditorHeader.js.map +1 -1
- package/dist/editor/components/EntryCreateModal.d.ts.map +1 -1
- package/dist/editor/components/EntryCreateModal.js +21 -6
- package/dist/editor/components/EntryCreateModal.js.map +1 -1
- package/dist/editor/context/SWRProvider.d.ts +31 -0
- package/dist/editor/context/SWRProvider.d.ts.map +1 -0
- package/dist/editor/context/SWRProvider.js +12 -0
- package/dist/editor/context/SWRProvider.js.map +1 -0
- package/dist/editor/context/index.d.ts +3 -0
- package/dist/editor/context/index.d.ts.map +1 -1
- package/dist/editor/context/index.js +3 -0
- package/dist/editor/context/index.js.map +1 -1
- package/dist/editor/editor-utils.d.ts.map +1 -1
- package/dist/editor/editor-utils.js +8 -2
- package/dist/editor/editor-utils.js.map +1 -1
- package/dist/editor/fields/ImageField.d.ts.map +1 -1
- package/dist/editor/fields/ImageField.js +27 -3
- package/dist/editor/fields/ImageField.js.map +1 -1
- package/dist/editor/fields/ReferenceField.d.ts.map +1 -1
- package/dist/editor/fields/ReferenceField.js +52 -31
- package/dist/editor/fields/ReferenceField.js.map +1 -1
- package/dist/editor/hooks/__test__/test-utils.d.ts +38 -3
- package/dist/editor/hooks/__test__/test-utils.d.ts.map +1 -1
- package/dist/editor/hooks/__test__/test-utils.js +50 -4
- package/dist/editor/hooks/__test__/test-utils.js.map +1 -1
- package/dist/editor/hooks/index.d.ts +4 -1
- package/dist/editor/hooks/index.d.ts.map +1 -1
- package/dist/editor/hooks/index.js +4 -1
- package/dist/editor/hooks/index.js.map +1 -1
- package/dist/editor/hooks/useBranchManager.d.ts +21 -1
- package/dist/editor/hooks/useBranchManager.d.ts.map +1 -1
- package/dist/editor/hooks/useBranchManager.js +68 -32
- package/dist/editor/hooks/useBranchManager.js.map +1 -1
- package/dist/editor/hooks/useBranchesData.d.ts +57 -0
- package/dist/editor/hooks/useBranchesData.d.ts.map +1 -0
- package/dist/editor/hooks/useBranchesData.js +69 -0
- package/dist/editor/hooks/useBranchesData.js.map +1 -0
- package/dist/editor/hooks/useCommentSystem.d.ts.map +1 -1
- package/dist/editor/hooks/useCommentSystem.js +24 -22
- package/dist/editor/hooks/useCommentSystem.js.map +1 -1
- package/dist/editor/hooks/useCommentsData.d.ts +29 -0
- package/dist/editor/hooks/useCommentsData.d.ts.map +1 -0
- package/dist/editor/hooks/useCommentsData.js +40 -0
- package/dist/editor/hooks/useCommentsData.js.map +1 -0
- package/dist/editor/hooks/useDraftManager.d.ts.map +1 -1
- package/dist/editor/hooks/useDraftManager.js +32 -18
- package/dist/editor/hooks/useDraftManager.js.map +1 -1
- package/dist/editor/hooks/useEntriesData.d.ts +35 -0
- package/dist/editor/hooks/useEntriesData.d.ts.map +1 -0
- package/dist/editor/hooks/useEntriesData.js +96 -0
- package/dist/editor/hooks/useEntriesData.js.map +1 -0
- package/dist/editor/hooks/useEntryManager.d.ts +8 -15
- package/dist/editor/hooks/useEntryManager.d.ts.map +1 -1
- package/dist/editor/hooks/useEntryManager.js +259 -117
- package/dist/editor/hooks/useEntryManager.js.map +1 -1
- package/dist/editor/hooks/usePermissionManager.d.ts +2 -2
- package/dist/editor/hooks/usePermissionManager.d.ts.map +1 -1
- package/dist/editor/hooks/usePermissionManager.js +11 -9
- package/dist/editor/hooks/usePermissionManager.js.map +1 -1
- package/dist/editor/hooks/useReferenceResolution.d.ts.map +1 -1
- package/dist/editor/hooks/useReferenceResolution.js +24 -1
- package/dist/editor/hooks/useReferenceResolution.js.map +1 -1
- package/dist/editor/media/MediaLibraryBody.d.ts.map +1 -1
- package/dist/editor/media/MediaLibraryBody.js +12 -2
- package/dist/editor/media/MediaLibraryBody.js.map +1 -1
- package/dist/editor/permission-manager/GroupSelector.d.ts.map +1 -1
- package/dist/editor/permission-manager/GroupSelector.js +7 -2
- package/dist/editor/permission-manager/GroupSelector.js.map +1 -1
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +3 -3
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts.map +1 -1
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +1 -0
- package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js.map +1 -1
- package/dist/editor/permission-manager/types.d.ts +6 -4
- package/dist/editor/permission-manager/types.d.ts.map +1 -1
- package/dist/editor/test-setup.js +8 -0
- package/dist/editor/test-setup.js.map +1 -1
- package/dist/git-manager.d.ts +126 -9
- package/dist/git-manager.d.ts.map +1 -1
- package/dist/git-manager.js +251 -27
- package/dist/git-manager.js.map +1 -1
- package/dist/github-service.d.ts.map +1 -1
- package/dist/github-service.js +13 -7
- package/dist/github-service.js.map +1 -1
- package/dist/http/handler.d.ts.map +1 -1
- package/dist/http/handler.js +8 -2
- package/dist/http/handler.js.map +1 -1
- package/dist/operating-mode/client-unsafe-strategy.d.ts.map +1 -1
- package/dist/operating-mode/client-unsafe-strategy.js +7 -8
- package/dist/operating-mode/client-unsafe-strategy.js.map +1 -1
- package/dist/operating-mode/deployment-name.d.ts +28 -0
- package/dist/operating-mode/deployment-name.d.ts.map +1 -0
- package/dist/operating-mode/deployment-name.js +78 -0
- package/dist/operating-mode/deployment-name.js.map +1 -0
- package/dist/operating-mode/index.d.ts +5 -1
- package/dist/operating-mode/index.d.ts.map +1 -1
- package/dist/operating-mode/index.js +7 -1
- package/dist/operating-mode/index.js.map +1 -1
- package/dist/operating-mode/types.d.ts +13 -3
- package/dist/operating-mode/types.d.ts.map +1 -1
- package/dist/paths/branch-name.d.ts +38 -0
- package/dist/paths/branch-name.d.ts.map +1 -1
- package/dist/paths/branch-name.js +46 -0
- package/dist/paths/branch-name.js.map +1 -1
- package/dist/paths/index.d.ts +1 -1
- package/dist/paths/index.d.ts.map +1 -1
- package/dist/paths/index.js +1 -1
- package/dist/paths/index.js.map +1 -1
- package/dist/reference-resolver.d.ts +6 -2
- package/dist/reference-resolver.d.ts.map +1 -1
- package/dist/reference-resolver.js +8 -1
- package/dist/reference-resolver.js.map +1 -1
- package/dist/schema/meta-loader.d.ts.map +1 -1
- package/dist/schema/meta-loader.js +10 -2
- package/dist/schema/meta-loader.js.map +1 -1
- package/dist/schema/schema-store.d.ts +11 -2
- package/dist/schema/schema-store.d.ts.map +1 -1
- package/dist/schema/schema-store.js +23 -11
- package/dist/schema/schema-store.js.map +1 -1
- package/dist/settings-workspace.d.ts +0 -15
- package/dist/settings-workspace.d.ts.map +1 -1
- package/dist/settings-workspace.js +97 -1
- package/dist/settings-workspace.js.map +1 -1
- package/dist/types.d.ts +63 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/debug.d.ts +11 -0
- package/dist/utils/debug.d.ts.map +1 -1
- package/dist/utils/debug.js +12 -1
- package/dist/utils/debug.js.map +1 -1
- package/dist/utils/git.d.ts +38 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +69 -0
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/logger.d.ts +71 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +85 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/validation/entry-type-reference-validator.d.ts +33 -0
- package/dist/validation/entry-type-reference-validator.d.ts.map +1 -0
- package/dist/validation/entry-type-reference-validator.js +110 -0
- package/dist/validation/entry-type-reference-validator.js.map +1 -0
- package/dist/worker/cms-worker.d.ts +232 -3
- package/dist/worker/cms-worker.d.ts.map +1 -1
- package/dist/worker/cms-worker.js +856 -87
- package/dist/worker/cms-worker.js.map +1 -1
- package/dist/worker/log.d.ts +61 -0
- package/dist/worker/log.d.ts.map +1 -0
- package/dist/worker/log.js +74 -0
- package/dist/worker/log.js.map +1 -0
- package/package.json +3 -1
package/dist/cli/cli.js
CHANGED
|
@@ -95,22 +95,50 @@ import { z as z4 } from "zod";
|
|
|
95
95
|
import { z as z5 } from "zod";
|
|
96
96
|
import { join, normalize } from "pathe";
|
|
97
97
|
import fs2 from "node:fs/promises";
|
|
98
|
+
import fs22 from "node:fs/promises";
|
|
98
99
|
import path2 from "node:path";
|
|
99
100
|
import { fileURLToPath } from "node:url";
|
|
100
|
-
import path3 from "node:path";
|
|
101
101
|
import fs3 from "node:fs/promises";
|
|
102
|
+
import path3 from "node:path";
|
|
103
|
+
import { simpleGit } from "simple-git";
|
|
102
104
|
import path4 from "node:path";
|
|
103
105
|
import fs4 from "node:fs/promises";
|
|
104
106
|
import path5 from "node:path";
|
|
105
|
-
import crypto from "node:crypto";
|
|
106
107
|
import fs5 from "node:fs/promises";
|
|
107
108
|
import path6 from "node:path";
|
|
109
|
+
import crypto from "node:crypto";
|
|
110
|
+
import fs6 from "node:fs/promises";
|
|
111
|
+
import path7 from "node:path";
|
|
108
112
|
import * as p from "@clack/prompts";
|
|
109
113
|
import { createJiti } from "jiti";
|
|
110
|
-
import
|
|
111
|
-
|
|
114
|
+
import path8 from "node:path";
|
|
115
|
+
function getErrorMessage2(err) {
|
|
116
|
+
if (err instanceof Error) {
|
|
117
|
+
return err.message;
|
|
118
|
+
}
|
|
119
|
+
if (typeof err === "string") {
|
|
120
|
+
return err;
|
|
121
|
+
}
|
|
122
|
+
return String(err);
|
|
123
|
+
}
|
|
124
|
+
function isNodeError2(err) {
|
|
125
|
+
return err instanceof Error && "code" in err;
|
|
126
|
+
}
|
|
127
|
+
function isNotFoundError2(err) {
|
|
128
|
+
return isNodeError2(err) && err.code === "ENOENT";
|
|
129
|
+
}
|
|
130
|
+
async function filePathExists2(filePath) {
|
|
131
|
+
try {
|
|
132
|
+
await fs2.stat(filePath);
|
|
133
|
+
return true;
|
|
134
|
+
} catch (err) {
|
|
135
|
+
if (isNotFoundError2(err))
|
|
136
|
+
return false;
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
112
140
|
async function readTemplate(name) {
|
|
113
|
-
return
|
|
141
|
+
return fs22.readFile(path2.join(TEMPLATES_DIR, name), "utf-8");
|
|
114
142
|
}
|
|
115
143
|
async function canopyCmsConfig(options) {
|
|
116
144
|
const template = await readTemplate("canopycms.config.ts.template");
|
|
@@ -161,23 +189,142 @@ async function nextConfig(options) {
|
|
|
161
189
|
const templateName = options.staticBuild ? "next.config-static.ts.template" : "next.config.ts.template";
|
|
162
190
|
return readTemplate(templateName);
|
|
163
191
|
}
|
|
164
|
-
async function dockerfileCms() {
|
|
165
|
-
|
|
192
|
+
async function dockerfileCms(options) {
|
|
193
|
+
const template = await readTemplate("Dockerfile.cms.template");
|
|
194
|
+
return template.replace("{{DOCKER_COPY}}", options.copy).replace("{{DOCKER_INSTALL}}", options.install).replace("{{DOCKER_BUILD}}", options.build);
|
|
166
195
|
}
|
|
167
196
|
async function dockerignore() {
|
|
168
197
|
return readTemplate("dockerignore.template");
|
|
169
198
|
}
|
|
170
|
-
async function githubWorkflowCms() {
|
|
171
|
-
|
|
199
|
+
async function githubWorkflowCms(options) {
|
|
200
|
+
const template = await readTemplate("deploy-cms.yml.template");
|
|
201
|
+
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);
|
|
202
|
+
}
|
|
203
|
+
async function cdkJson() {
|
|
204
|
+
return readTemplate("cdk.json.template");
|
|
205
|
+
}
|
|
206
|
+
async function cdkApp(options) {
|
|
207
|
+
const template = await readTemplate("cdk-app.ts.template");
|
|
208
|
+
return template.replace("{{STACK_NAME}}", SCAFFOLD_STACK_NAME).replace("{{GITHUB_OWNER}}", options.githubOwner).replace("{{GITHUB_REPO}}", options.githubRepo);
|
|
209
|
+
}
|
|
210
|
+
async function cmsStack() {
|
|
211
|
+
return readTemplate("cms-stack.ts.template");
|
|
212
|
+
}
|
|
213
|
+
function commandsFor(manager) {
|
|
214
|
+
return COMMANDS[manager];
|
|
215
|
+
}
|
|
216
|
+
async function detectPackageManager(projectDir) {
|
|
217
|
+
for (const dir of await workspaceSearchPath(projectDir)) {
|
|
218
|
+
const declared = await readDeclaredPackageManager(dir);
|
|
219
|
+
if (declared)
|
|
220
|
+
return declared;
|
|
221
|
+
if (await filePathExists2(path3.join(dir, "pnpm-lock.yaml")))
|
|
222
|
+
return "pnpm";
|
|
223
|
+
if (await filePathExists2(path3.join(dir, "yarn.lock"))) {
|
|
224
|
+
return await isYarnBerryLockfile(dir) ? "yarn-berry" : "yarn";
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return "npm";
|
|
228
|
+
}
|
|
229
|
+
async function workspaceSearchPath(projectDir) {
|
|
230
|
+
const start = path3.resolve(projectDir);
|
|
231
|
+
const dirs = [start];
|
|
232
|
+
if (!await filePathExists2(path3.join(start, "package.json")))
|
|
233
|
+
return dirs;
|
|
234
|
+
let dir = start;
|
|
235
|
+
for (let depth = 0; depth < MAX_WORKSPACE_DEPTH; depth++) {
|
|
236
|
+
if (await filePathExists2(path3.join(dir, ".git")))
|
|
237
|
+
break;
|
|
238
|
+
const parent = path3.dirname(dir);
|
|
239
|
+
if (parent === dir)
|
|
240
|
+
break;
|
|
241
|
+
dir = parent;
|
|
242
|
+
dirs.push(dir);
|
|
243
|
+
}
|
|
244
|
+
return dirs;
|
|
245
|
+
}
|
|
246
|
+
async function readDeclaredPackageManager(projectDir) {
|
|
247
|
+
const pkg = await readPackageJson(projectDir);
|
|
248
|
+
const declared = pkg?.["packageManager"];
|
|
249
|
+
if (typeof declared !== "string")
|
|
250
|
+
return null;
|
|
251
|
+
if (declared.startsWith("pnpm@"))
|
|
252
|
+
return "pnpm";
|
|
253
|
+
if (declared.startsWith("npm@"))
|
|
254
|
+
return "npm";
|
|
255
|
+
if (declared.startsWith("yarn@")) {
|
|
256
|
+
const major = Number.parseInt(declared.slice("yarn@".length), 10);
|
|
257
|
+
if (Number.isFinite(major) && major >= 2)
|
|
258
|
+
return "yarn-berry";
|
|
259
|
+
if (Number.isFinite(major))
|
|
260
|
+
return "yarn";
|
|
261
|
+
return await isYarnBerryLockfile(projectDir) ? "yarn-berry" : "yarn";
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
async function isYarnBerryLockfile(projectDir) {
|
|
266
|
+
try {
|
|
267
|
+
const content = await fs3.readFile(path3.join(projectDir, "yarn.lock"), "utf-8");
|
|
268
|
+
return content.includes("__metadata:");
|
|
269
|
+
} catch {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async function detectDefaultBranch(projectDir) {
|
|
274
|
+
const head = await gitRaw(projectDir, ["symbolic-ref", "--short", "refs/remotes/origin/HEAD"]);
|
|
275
|
+
if (!head)
|
|
276
|
+
return "main";
|
|
277
|
+
const slash = head.indexOf("/");
|
|
278
|
+
const branch = slash === -1 ? head : head.slice(slash + 1);
|
|
279
|
+
return branch || "main";
|
|
280
|
+
}
|
|
281
|
+
async function detectGitHubRepo(projectDir) {
|
|
282
|
+
const url = await gitRaw(projectDir, ["remote", "get-url", "origin"]);
|
|
283
|
+
if (!url)
|
|
284
|
+
return null;
|
|
285
|
+
const match = /github\.com[:/]+([^/]+)\/(.+?)(?:\.git)?\/?$/.exec(url);
|
|
286
|
+
if (!match)
|
|
287
|
+
return null;
|
|
288
|
+
return { owner: match[1], repo: match[2] };
|
|
289
|
+
}
|
|
290
|
+
async function gitRaw(projectDir, args) {
|
|
291
|
+
try {
|
|
292
|
+
const output = await simpleGit({ baseDir: projectDir }).raw(args);
|
|
293
|
+
const trimmed = output.trim();
|
|
294
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
295
|
+
} catch {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
async function readPackageJson(projectDir) {
|
|
300
|
+
try {
|
|
301
|
+
const raw = await fs3.readFile(path3.join(projectDir, "package.json"), "utf-8");
|
|
302
|
+
const parsed = JSON.parse(raw);
|
|
303
|
+
return typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
304
|
+
} catch {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
async function missingCdkDependencies(projectDir) {
|
|
309
|
+
const pkg = await readPackageJson(projectDir);
|
|
310
|
+
const declared = /* @__PURE__ */ new Set();
|
|
311
|
+
for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
312
|
+
const section = pkg?.[field];
|
|
313
|
+
if (typeof section === "object" && section !== null) {
|
|
314
|
+
for (const name of Object.keys(section))
|
|
315
|
+
declared.add(name);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return CDK_DEPENDENCIES.filter((name) => !declared.has(name));
|
|
172
319
|
}
|
|
173
320
|
function getTaskQueueDir(config) {
|
|
174
321
|
switch (config.mode) {
|
|
175
322
|
case "prod": {
|
|
176
323
|
const workspace = process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE;
|
|
177
|
-
return
|
|
324
|
+
return path4.join(path4.resolve(workspace), ".tasks");
|
|
178
325
|
}
|
|
179
326
|
case "dev": {
|
|
180
|
-
return
|
|
327
|
+
return path4.join(process.cwd(), ".canopy-dev", ".tasks");
|
|
181
328
|
}
|
|
182
329
|
}
|
|
183
330
|
}
|
|
@@ -185,24 +332,24 @@ function createDebugLogger(options) {
|
|
|
185
332
|
return new DebugLogger(options);
|
|
186
333
|
}
|
|
187
334
|
async function atomicWriteFile(filePath, content) {
|
|
188
|
-
const dir =
|
|
189
|
-
await
|
|
335
|
+
const dir = path5.dirname(filePath);
|
|
336
|
+
await fs4.mkdir(dir, { recursive: true });
|
|
190
337
|
const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
191
|
-
await
|
|
338
|
+
await fs4.writeFile(tempPath, content, typeof content === "string" ? "utf-8" : void 0);
|
|
192
339
|
try {
|
|
193
|
-
await
|
|
340
|
+
await fs4.rename(tempPath, filePath);
|
|
194
341
|
} catch (err) {
|
|
195
|
-
await
|
|
342
|
+
await fs4.unlink(tempPath).catch(() => {
|
|
196
343
|
});
|
|
197
344
|
throw err;
|
|
198
345
|
}
|
|
199
346
|
}
|
|
200
|
-
function
|
|
347
|
+
function isNotFoundError22(err) {
|
|
201
348
|
return err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
202
349
|
}
|
|
203
350
|
async function enqueueTask(taskDir, task, logger = nullLogger) {
|
|
204
351
|
const id = crypto.randomUUID();
|
|
205
|
-
const pendingDir =
|
|
352
|
+
const pendingDir = path6.join(taskDir, "pending");
|
|
206
353
|
const queuedTask = {
|
|
207
354
|
id,
|
|
208
355
|
action: task.action,
|
|
@@ -212,17 +359,17 @@ async function enqueueTask(taskDir, task, logger = nullLogger) {
|
|
|
212
359
|
retryCount: 0,
|
|
213
360
|
maxRetries: task.maxRetries ?? DEFAULT_MAX_RETRIES
|
|
214
361
|
};
|
|
215
|
-
const filePath =
|
|
362
|
+
const filePath = path6.join(pendingDir, `${id}.json`);
|
|
216
363
|
await atomicWriteFile(filePath, JSON.stringify(queuedTask, null, 2));
|
|
217
364
|
logger.debug("Enqueued task", { id, action: task.action });
|
|
218
365
|
return id;
|
|
219
366
|
}
|
|
220
367
|
async function dequeueTask(taskDir, logger = nullLogger) {
|
|
221
|
-
const pendingDir =
|
|
222
|
-
const processingDir =
|
|
368
|
+
const pendingDir = path6.join(taskDir, "pending");
|
|
369
|
+
const processingDir = path6.join(taskDir, "processing");
|
|
223
370
|
let files;
|
|
224
371
|
try {
|
|
225
|
-
files = await
|
|
372
|
+
files = await fs5.readdir(pendingDir);
|
|
226
373
|
} catch {
|
|
227
374
|
return null;
|
|
228
375
|
}
|
|
@@ -233,7 +380,7 @@ async function dequeueTask(taskDir, logger = nullLogger) {
|
|
|
233
380
|
const tasks = [];
|
|
234
381
|
for (const fileName of jsonFiles) {
|
|
235
382
|
try {
|
|
236
|
-
const content = await
|
|
383
|
+
const content = await fs5.readFile(path6.join(pendingDir, fileName), "utf-8");
|
|
237
384
|
const task = parseTaskJson(content);
|
|
238
385
|
if (!task) {
|
|
239
386
|
await moveToCorrupt(taskDir, pendingDir, fileName, "Invalid JSON", logger);
|
|
@@ -244,7 +391,7 @@ async function dequeueTask(taskDir, logger = nullLogger) {
|
|
|
244
391
|
}
|
|
245
392
|
tasks.push({ fileName, task });
|
|
246
393
|
} catch (err) {
|
|
247
|
-
if (
|
|
394
|
+
if (isNotFoundError22(err))
|
|
248
395
|
continue;
|
|
249
396
|
throw err;
|
|
250
397
|
}
|
|
@@ -254,21 +401,21 @@ async function dequeueTask(taskDir, logger = nullLogger) {
|
|
|
254
401
|
tasks.sort((a, b) => a.task.createdAt.localeCompare(b.task.createdAt) || a.task.id.localeCompare(b.task.id));
|
|
255
402
|
for (const { fileName, task } of tasks) {
|
|
256
403
|
if (await taskExistsIn(taskDir, task.id, ["completed", "failed"])) {
|
|
257
|
-
await
|
|
404
|
+
await fs5.unlink(path6.join(pendingDir, fileName)).catch(() => {
|
|
258
405
|
});
|
|
259
406
|
logger.debug("Skipped already-finished task", { id: task.id });
|
|
260
407
|
continue;
|
|
261
408
|
}
|
|
262
|
-
const sourcePath =
|
|
263
|
-
const destPath =
|
|
409
|
+
const sourcePath = path6.join(pendingDir, fileName);
|
|
410
|
+
const destPath = path6.join(processingDir, fileName);
|
|
264
411
|
try {
|
|
265
412
|
task.status = "processing";
|
|
266
413
|
await atomicWriteFile(destPath, JSON.stringify(task, null, 2));
|
|
267
|
-
await
|
|
414
|
+
await fs5.unlink(sourcePath);
|
|
268
415
|
logger.debug("Dequeued task", { id: task.id, action: task.action });
|
|
269
416
|
return task;
|
|
270
417
|
} catch (err) {
|
|
271
|
-
if (
|
|
418
|
+
if (isNotFoundError22(err))
|
|
272
419
|
continue;
|
|
273
420
|
throw err;
|
|
274
421
|
}
|
|
@@ -276,22 +423,22 @@ async function dequeueTask(taskDir, logger = nullLogger) {
|
|
|
276
423
|
return null;
|
|
277
424
|
}
|
|
278
425
|
async function completeTask(taskDir, taskId, result, logger = nullLogger) {
|
|
279
|
-
const processingPath =
|
|
280
|
-
const completedDir =
|
|
281
|
-
const completedPath =
|
|
426
|
+
const processingPath = path6.join(taskDir, "processing", `${taskId}.json`);
|
|
427
|
+
const completedDir = path6.join(taskDir, "completed");
|
|
428
|
+
const completedPath = path6.join(completedDir, `${taskId}.json`);
|
|
282
429
|
let task;
|
|
283
430
|
try {
|
|
284
|
-
const content = await
|
|
431
|
+
const content = await fs5.readFile(processingPath, "utf-8");
|
|
285
432
|
const parsed = parseTaskJson(content);
|
|
286
433
|
if (!parsed) {
|
|
287
434
|
logger.debug("Corrupt task file in processing, removing", { id: taskId });
|
|
288
|
-
await
|
|
435
|
+
await fs5.unlink(processingPath).catch(() => {
|
|
289
436
|
});
|
|
290
437
|
return;
|
|
291
438
|
}
|
|
292
439
|
task = parsed;
|
|
293
440
|
} catch (err) {
|
|
294
|
-
if (
|
|
441
|
+
if (isNotFoundError22(err))
|
|
295
442
|
return;
|
|
296
443
|
throw err;
|
|
297
444
|
}
|
|
@@ -299,27 +446,27 @@ async function completeTask(taskDir, taskId, result, logger = nullLogger) {
|
|
|
299
446
|
task.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
300
447
|
task.result = result;
|
|
301
448
|
await atomicWriteFile(completedPath, JSON.stringify(task, null, 2));
|
|
302
|
-
await
|
|
449
|
+
await fs5.unlink(processingPath).catch(() => {
|
|
303
450
|
});
|
|
304
451
|
logger.debug("Completed task", { id: taskId });
|
|
305
452
|
}
|
|
306
453
|
async function failTask(taskDir, taskId, error, logger = nullLogger) {
|
|
307
|
-
const processingPath =
|
|
308
|
-
const failedDir =
|
|
309
|
-
const failedPath =
|
|
454
|
+
const processingPath = path6.join(taskDir, "processing", `${taskId}.json`);
|
|
455
|
+
const failedDir = path6.join(taskDir, "failed");
|
|
456
|
+
const failedPath = path6.join(failedDir, `${taskId}.json`);
|
|
310
457
|
let task;
|
|
311
458
|
try {
|
|
312
|
-
const content = await
|
|
459
|
+
const content = await fs5.readFile(processingPath, "utf-8");
|
|
313
460
|
const parsed = parseTaskJson(content);
|
|
314
461
|
if (!parsed) {
|
|
315
462
|
logger.debug("Corrupt task file in processing, removing", { id: taskId });
|
|
316
|
-
await
|
|
463
|
+
await fs5.unlink(processingPath).catch(() => {
|
|
317
464
|
});
|
|
318
465
|
return;
|
|
319
466
|
}
|
|
320
467
|
task = parsed;
|
|
321
468
|
} catch (err) {
|
|
322
|
-
if (
|
|
469
|
+
if (isNotFoundError22(err))
|
|
323
470
|
return;
|
|
324
471
|
throw err;
|
|
325
472
|
}
|
|
@@ -327,27 +474,27 @@ async function failTask(taskDir, taskId, error, logger = nullLogger) {
|
|
|
327
474
|
task.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
328
475
|
task.error = error;
|
|
329
476
|
await atomicWriteFile(failedPath, JSON.stringify(task, null, 2));
|
|
330
|
-
await
|
|
477
|
+
await fs5.unlink(processingPath).catch(() => {
|
|
331
478
|
});
|
|
332
479
|
logger.debug("Failed task", { id: taskId, error });
|
|
333
480
|
}
|
|
334
481
|
async function retryTask(taskDir, taskId, error, logger = nullLogger) {
|
|
335
|
-
const processingPath =
|
|
336
|
-
const pendingDir =
|
|
337
|
-
const pendingPath =
|
|
482
|
+
const processingPath = path6.join(taskDir, "processing", `${taskId}.json`);
|
|
483
|
+
const pendingDir = path6.join(taskDir, "pending");
|
|
484
|
+
const pendingPath = path6.join(pendingDir, `${taskId}.json`);
|
|
338
485
|
let task;
|
|
339
486
|
try {
|
|
340
|
-
const content = await
|
|
487
|
+
const content = await fs5.readFile(processingPath, "utf-8");
|
|
341
488
|
const parsed = parseTaskJson(content);
|
|
342
489
|
if (!parsed) {
|
|
343
490
|
logger.debug("Corrupt task file in processing, removing", { id: taskId });
|
|
344
|
-
await
|
|
491
|
+
await fs5.unlink(processingPath).catch(() => {
|
|
345
492
|
});
|
|
346
493
|
return;
|
|
347
494
|
}
|
|
348
495
|
task = parsed;
|
|
349
496
|
} catch (err) {
|
|
350
|
-
if (
|
|
497
|
+
if (isNotFoundError22(err))
|
|
351
498
|
return;
|
|
352
499
|
throw err;
|
|
353
500
|
}
|
|
@@ -358,17 +505,17 @@ async function retryTask(taskDir, taskId, error, logger = nullLogger) {
|
|
|
358
505
|
task.retryAfter = new Date(Date.now() + backoffMs).toISOString();
|
|
359
506
|
task.error = error;
|
|
360
507
|
await atomicWriteFile(pendingPath, JSON.stringify(task, null, 2));
|
|
361
|
-
await
|
|
508
|
+
await fs5.unlink(processingPath).catch(() => {
|
|
362
509
|
});
|
|
363
510
|
logger.debug("Retrying task", { id: taskId, retryCount, backoffMs });
|
|
364
511
|
}
|
|
365
512
|
async function requeueFailedTask(taskDir, taskId, logger = nullLogger) {
|
|
366
|
-
const failedPath =
|
|
513
|
+
const failedPath = path6.join(taskDir, "failed", `${taskId}.json`);
|
|
367
514
|
let content;
|
|
368
515
|
try {
|
|
369
|
-
content = await
|
|
516
|
+
content = await fs5.readFile(failedPath, "utf-8");
|
|
370
517
|
} catch (err) {
|
|
371
|
-
if (
|
|
518
|
+
if (isNotFoundError22(err))
|
|
372
519
|
return { error: "not-found" };
|
|
373
520
|
throw err;
|
|
374
521
|
}
|
|
@@ -385,37 +532,37 @@ async function requeueFailedTask(taskDir, taskId, logger = nullLogger) {
|
|
|
385
532
|
retryCount: 0,
|
|
386
533
|
maxRetries: original.maxRetries
|
|
387
534
|
};
|
|
388
|
-
const pendingPath =
|
|
535
|
+
const pendingPath = path6.join(taskDir, "pending", `${newTaskId}.json`);
|
|
389
536
|
await atomicWriteFile(pendingPath, JSON.stringify(newTask, null, 2));
|
|
390
|
-
await
|
|
537
|
+
await fs5.unlink(failedPath).catch(() => {
|
|
391
538
|
});
|
|
392
539
|
logger.debug("Requeued failed task", { originalId: taskId, newTaskId });
|
|
393
540
|
return { newTaskId };
|
|
394
541
|
}
|
|
395
542
|
async function recoverOrphanedTasks(taskDir, maxAgeMs = 5 * 6e4, logger = nullLogger) {
|
|
396
|
-
const processingDir =
|
|
397
|
-
const pendingDir =
|
|
543
|
+
const processingDir = path6.join(taskDir, "processing");
|
|
544
|
+
const pendingDir = path6.join(taskDir, "pending");
|
|
398
545
|
let files;
|
|
399
546
|
try {
|
|
400
|
-
files = await
|
|
547
|
+
files = await fs5.readdir(processingDir);
|
|
401
548
|
} catch {
|
|
402
549
|
return 0;
|
|
403
550
|
}
|
|
404
551
|
const now = Date.now();
|
|
405
552
|
let recovered = 0;
|
|
406
553
|
for (const fileName of files.filter((f) => f.endsWith(".json"))) {
|
|
407
|
-
const filePath =
|
|
554
|
+
const filePath = path6.join(processingDir, fileName);
|
|
408
555
|
try {
|
|
409
|
-
const stat = await
|
|
556
|
+
const stat = await fs5.stat(filePath);
|
|
410
557
|
if (now - stat.mtimeMs >= maxAgeMs) {
|
|
411
|
-
const content = await
|
|
558
|
+
const content = await fs5.readFile(filePath, "utf-8");
|
|
412
559
|
const task = parseTaskJson(content);
|
|
413
560
|
if (!task) {
|
|
414
561
|
await moveToCorrupt(taskDir, processingDir, fileName, "Invalid JSON during recovery", logger);
|
|
415
562
|
continue;
|
|
416
563
|
}
|
|
417
564
|
if (await taskExistsIn(taskDir, task.id, ["completed", "failed"])) {
|
|
418
|
-
await
|
|
565
|
+
await fs5.unlink(filePath).catch(() => {
|
|
419
566
|
});
|
|
420
567
|
logger.debug("Cleaned up orphaned task (already finished)", {
|
|
421
568
|
id: task.id
|
|
@@ -423,8 +570,8 @@ async function recoverOrphanedTasks(taskDir, maxAgeMs = 5 * 6e4, logger = nullLo
|
|
|
423
570
|
continue;
|
|
424
571
|
}
|
|
425
572
|
task.status = "pending";
|
|
426
|
-
await atomicWriteFile(
|
|
427
|
-
await
|
|
573
|
+
await atomicWriteFile(path6.join(pendingDir, fileName), JSON.stringify(task, null, 2));
|
|
574
|
+
await fs5.unlink(filePath);
|
|
428
575
|
logger.debug("Recovered orphaned task", {
|
|
429
576
|
id: task.id,
|
|
430
577
|
action: task.action
|
|
@@ -432,7 +579,7 @@ async function recoverOrphanedTasks(taskDir, maxAgeMs = 5 * 6e4, logger = nullLo
|
|
|
432
579
|
recovered++;
|
|
433
580
|
}
|
|
434
581
|
} catch (err) {
|
|
435
|
-
if (
|
|
582
|
+
if (isNotFoundError22(err))
|
|
436
583
|
continue;
|
|
437
584
|
logger.debug("Failed to recover task", { fileName });
|
|
438
585
|
}
|
|
@@ -443,19 +590,19 @@ async function cleanupOldTasks(taskDir, maxAgeMs = 30 * 24 * 60 * 6e4, logger =
|
|
|
443
590
|
const now = Date.now();
|
|
444
591
|
let cleaned = 0;
|
|
445
592
|
for (const subdir of ["completed", "failed"]) {
|
|
446
|
-
const dir =
|
|
593
|
+
const dir = path6.join(taskDir, subdir);
|
|
447
594
|
let files;
|
|
448
595
|
try {
|
|
449
|
-
files = await
|
|
596
|
+
files = await fs5.readdir(dir);
|
|
450
597
|
} catch {
|
|
451
598
|
continue;
|
|
452
599
|
}
|
|
453
600
|
for (const fileName of files.filter((f) => f.endsWith(".json"))) {
|
|
454
601
|
try {
|
|
455
|
-
const filePath =
|
|
456
|
-
const stat = await
|
|
602
|
+
const filePath = path6.join(dir, fileName);
|
|
603
|
+
const stat = await fs5.stat(filePath);
|
|
457
604
|
if (now - stat.mtimeMs >= maxAgeMs) {
|
|
458
|
-
await
|
|
605
|
+
await fs5.unlink(filePath);
|
|
459
606
|
cleaned++;
|
|
460
607
|
}
|
|
461
608
|
} catch {
|
|
@@ -469,9 +616,9 @@ async function cleanupOldTasks(taskDir, maxAgeMs = 30 * 24 * 60 * 6e4, logger =
|
|
|
469
616
|
}
|
|
470
617
|
async function getTask(taskDir, taskId) {
|
|
471
618
|
for (const subdir of ["completed", "failed", "processing", "pending"]) {
|
|
472
|
-
const filePath =
|
|
619
|
+
const filePath = path6.join(taskDir, subdir, `${taskId}.json`);
|
|
473
620
|
try {
|
|
474
|
-
const content = await
|
|
621
|
+
const content = await fs5.readFile(filePath, "utf-8");
|
|
475
622
|
return parseTaskJson(content);
|
|
476
623
|
} catch {
|
|
477
624
|
continue;
|
|
@@ -480,10 +627,10 @@ async function getTask(taskDir, taskId) {
|
|
|
480
627
|
return null;
|
|
481
628
|
}
|
|
482
629
|
async function listTasks(taskDir, status, limit = 100) {
|
|
483
|
-
const dir =
|
|
630
|
+
const dir = path6.join(taskDir, status);
|
|
484
631
|
let files;
|
|
485
632
|
try {
|
|
486
|
-
files = await
|
|
633
|
+
files = await fs5.readdir(dir);
|
|
487
634
|
} catch {
|
|
488
635
|
return [];
|
|
489
636
|
}
|
|
@@ -492,7 +639,7 @@ async function listTasks(taskDir, status, limit = 100) {
|
|
|
492
639
|
if (tasks.length >= limit)
|
|
493
640
|
break;
|
|
494
641
|
try {
|
|
495
|
-
const content = await
|
|
642
|
+
const content = await fs5.readFile(path6.join(dir, fileName), "utf-8");
|
|
496
643
|
const task = parseTaskJson(content);
|
|
497
644
|
if (task)
|
|
498
645
|
tasks.push(task);
|
|
@@ -512,9 +659,9 @@ async function getQueueStats(taskDir) {
|
|
|
512
659
|
corrupt: 0
|
|
513
660
|
};
|
|
514
661
|
for (const status of ["pending", "processing", "completed", "failed", "corrupt"]) {
|
|
515
|
-
const dir =
|
|
662
|
+
const dir = path6.join(taskDir, status);
|
|
516
663
|
try {
|
|
517
|
-
const files = await
|
|
664
|
+
const files = await fs5.readdir(dir);
|
|
518
665
|
stats[status] = files.filter((f) => f.endsWith(".json")).length;
|
|
519
666
|
} catch {
|
|
520
667
|
}
|
|
@@ -522,20 +669,20 @@ async function getQueueStats(taskDir) {
|
|
|
522
669
|
return stats;
|
|
523
670
|
}
|
|
524
671
|
async function listCorruptTaskFiles(taskDir, limit = 50, logger = nullLogger) {
|
|
525
|
-
const corruptDir =
|
|
672
|
+
const corruptDir = path6.join(taskDir, "corrupt");
|
|
526
673
|
let files;
|
|
527
674
|
try {
|
|
528
|
-
files = await
|
|
675
|
+
files = await fs5.readdir(corruptDir);
|
|
529
676
|
} catch (err) {
|
|
530
|
-
if (
|
|
677
|
+
if (isNotFoundError22(err))
|
|
531
678
|
return [];
|
|
532
679
|
throw err;
|
|
533
680
|
}
|
|
534
681
|
const entries = [];
|
|
535
682
|
for (const fileName of files.filter((f) => f.endsWith(".json"))) {
|
|
536
|
-
const filePath =
|
|
683
|
+
const filePath = path6.join(corruptDir, fileName);
|
|
537
684
|
try {
|
|
538
|
-
const stat = await
|
|
685
|
+
const stat = await fs5.stat(filePath);
|
|
539
686
|
entries.push({
|
|
540
687
|
fileName,
|
|
541
688
|
size: stat.size,
|
|
@@ -543,7 +690,7 @@ async function listCorruptTaskFiles(taskDir, limit = 50, logger = nullLogger) {
|
|
|
543
690
|
rawSnippet: await readSnippet(filePath, 500)
|
|
544
691
|
});
|
|
545
692
|
} catch (err) {
|
|
546
|
-
if (
|
|
693
|
+
if (isNotFoundError22(err))
|
|
547
694
|
continue;
|
|
548
695
|
logger.debug("Failed to read corrupt task file", { fileName });
|
|
549
696
|
}
|
|
@@ -552,7 +699,7 @@ async function listCorruptTaskFiles(taskDir, limit = 50, logger = nullLogger) {
|
|
|
552
699
|
return entries.slice(0, limit);
|
|
553
700
|
}
|
|
554
701
|
async function readSnippet(filePath, maxBytes) {
|
|
555
|
-
const handle = await
|
|
702
|
+
const handle = await fs5.open(filePath, "r");
|
|
556
703
|
try {
|
|
557
704
|
const buffer = Buffer.alloc(maxBytes);
|
|
558
705
|
const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
|
|
@@ -575,7 +722,7 @@ function parseTaskJson(content) {
|
|
|
575
722
|
async function taskExistsIn(taskDir, taskId, subdirs) {
|
|
576
723
|
for (const subdir of subdirs) {
|
|
577
724
|
try {
|
|
578
|
-
await
|
|
725
|
+
await fs5.stat(path6.join(taskDir, subdir, `${taskId}.json`));
|
|
579
726
|
return true;
|
|
580
727
|
} catch {
|
|
581
728
|
}
|
|
@@ -583,16 +730,33 @@ async function taskExistsIn(taskDir, taskId, subdirs) {
|
|
|
583
730
|
return false;
|
|
584
731
|
}
|
|
585
732
|
async function moveToCorrupt(taskDir, sourceDir, fileName, reason, logger) {
|
|
586
|
-
const corruptDir =
|
|
733
|
+
const corruptDir = path6.join(taskDir, "corrupt");
|
|
587
734
|
try {
|
|
588
|
-
await
|
|
589
|
-
await
|
|
735
|
+
await fs5.mkdir(corruptDir, { recursive: true });
|
|
736
|
+
await fs5.rename(path6.join(sourceDir, fileName), path6.join(corruptDir, fileName));
|
|
590
737
|
logger.debug("Moved corrupt task file", { fileName, reason });
|
|
591
738
|
} catch {
|
|
592
|
-
await
|
|
739
|
+
await fs5.unlink(path6.join(sourceDir, fileName)).catch(() => {
|
|
593
740
|
});
|
|
594
741
|
}
|
|
595
742
|
}
|
|
743
|
+
function canopyLogWarn(...args) {
|
|
744
|
+
active.warn(...args);
|
|
745
|
+
}
|
|
746
|
+
function resolveDeploymentName(config, modeDefault) {
|
|
747
|
+
const envValue = process.env.CANOPYCMS_DEPLOYMENT_NAME?.trim();
|
|
748
|
+
const configValue = config.deploymentName;
|
|
749
|
+
if (envValue && configValue && envValue !== configValue && !warned) {
|
|
750
|
+
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.`);
|
|
751
|
+
warned = true;
|
|
752
|
+
}
|
|
753
|
+
const resolved = envValue || configValue || modeDefault;
|
|
754
|
+
if (!isValidDeploymentName(resolved)) {
|
|
755
|
+
const source = envValue ? "CANOPYCMS_DEPLOYMENT_NAME" : configValue ? "config.deploymentName" : "the mode default";
|
|
756
|
+
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'.`);
|
|
757
|
+
}
|
|
758
|
+
return resolved;
|
|
759
|
+
}
|
|
596
760
|
function operatingStrategy(mode) {
|
|
597
761
|
const cached = strategyCache.get(mode);
|
|
598
762
|
if (cached)
|
|
@@ -618,33 +782,8 @@ function assertAuthPluginAllowedForMode(plugin, mode) {
|
|
|
618
782
|
throw new Error("CanopyCMS: an auth plugin was configured with mode: 'prod' but does not affirm `verifiesCredentials: true`. This plugin performs no real credential verification, so anyone could impersonate any user (including admins). Configure a verifying auth plugin for production (e.g. createClerkAuthPlugin from 'canopycms-auth-clerk' with CLERK_SECRET_KEY set), or run with mode: 'dev' for local development.");
|
|
619
783
|
}
|
|
620
784
|
}
|
|
621
|
-
function getErrorMessage2(err) {
|
|
622
|
-
if (err instanceof Error) {
|
|
623
|
-
return err.message;
|
|
624
|
-
}
|
|
625
|
-
if (typeof err === "string") {
|
|
626
|
-
return err;
|
|
627
|
-
}
|
|
628
|
-
return String(err);
|
|
629
|
-
}
|
|
630
|
-
function isNodeError2(err) {
|
|
631
|
-
return err instanceof Error && "code" in err;
|
|
632
|
-
}
|
|
633
|
-
function isNotFoundError3(err) {
|
|
634
|
-
return isNodeError2(err) && err.code === "ENOENT";
|
|
635
|
-
}
|
|
636
|
-
async function filePathExists2(filePath) {
|
|
637
|
-
try {
|
|
638
|
-
await fs6.stat(filePath);
|
|
639
|
-
return true;
|
|
640
|
-
} catch (err) {
|
|
641
|
-
if (isNotFoundError3(err))
|
|
642
|
-
return false;
|
|
643
|
-
throw err;
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
785
|
async function writeFile(filePath, content, options) {
|
|
647
|
-
const relativePath =
|
|
786
|
+
const relativePath = path7.relative(process.cwd(), filePath);
|
|
648
787
|
if (await filePathExists2(filePath)) {
|
|
649
788
|
if (options.force) {
|
|
650
789
|
} else if (options.nonInteractive) {
|
|
@@ -661,8 +800,8 @@ async function writeFile(filePath, content, options) {
|
|
|
661
800
|
}
|
|
662
801
|
}
|
|
663
802
|
}
|
|
664
|
-
await
|
|
665
|
-
await
|
|
803
|
+
await fs6.mkdir(path7.dirname(filePath), { recursive: true });
|
|
804
|
+
await fs6.writeFile(filePath, content, "utf-8");
|
|
666
805
|
p.log.success(`created: ${relativePath}`);
|
|
667
806
|
return true;
|
|
668
807
|
}
|
|
@@ -714,24 +853,24 @@ async function init(options) {
|
|
|
714
853
|
}
|
|
715
854
|
const serverPageExt = staticBuild ? "page.server.tsx" : "page.tsx";
|
|
716
855
|
const serverRouteExt = staticBuild ? "route.server.ts" : "route.ts";
|
|
717
|
-
await writeFile(
|
|
718
|
-
await writeFile(
|
|
719
|
-
await writeFile(
|
|
720
|
-
await writeFile(
|
|
856
|
+
await writeFile(path7.join(projectDir, "canopycms.config.ts"), await canopyCmsConfig2({ mode, staticBuild }), writeOpts);
|
|
857
|
+
await writeFile(path7.join(projectDir, appDir, "lib/canopy.ts"), await canopyContext2({ configImport: configImportPath(appDir, 1), authProvider }), writeOpts);
|
|
858
|
+
await writeFile(path7.join(projectDir, appDir, "schemas.ts"), await schemasTemplate2(), writeOpts);
|
|
859
|
+
await writeFile(path7.join(projectDir, appDir, `api/canopycms/[...canopycms]/${serverRouteExt}`), await apiRoute2({
|
|
721
860
|
canopyImport: "../".repeat(3) + "lib/canopy"
|
|
722
861
|
}), writeOpts);
|
|
723
|
-
await writeFile(
|
|
862
|
+
await writeFile(path7.join(projectDir, appDir, `edit/${serverPageExt}`), await editPage2({ configImport: configImportPath(appDir, 1), authProvider }), writeOpts);
|
|
724
863
|
if (ai) {
|
|
725
|
-
await writeFile(
|
|
726
|
-
await writeFile(
|
|
864
|
+
await writeFile(path7.join(projectDir, appDir, "ai/config.ts"), await aiConfig2(), writeOpts);
|
|
865
|
+
await writeFile(path7.join(projectDir, appDir, `ai/[...path]/${serverRouteExt}`), await aiRoute2({ configImport: configImportPath(appDir, 2) }), writeOpts);
|
|
727
866
|
}
|
|
728
|
-
await writeFile(
|
|
729
|
-
await writeFile(
|
|
730
|
-
const gitignorePath =
|
|
867
|
+
await writeFile(path7.join(projectDir, "next.config.ts"), await nextConfig2({ staticBuild }), writeOpts);
|
|
868
|
+
await writeFile(path7.join(projectDir, "middleware.ts"), await middleware2({ authProvider }), writeOpts);
|
|
869
|
+
const gitignorePath = path7.join(projectDir, ".gitignore");
|
|
731
870
|
if (await filePathExists2(gitignorePath)) {
|
|
732
|
-
const content = await
|
|
871
|
+
const content = await fs6.readFile(gitignorePath, "utf-8");
|
|
733
872
|
if (!content.includes(".canopy-dev")) {
|
|
734
|
-
await
|
|
873
|
+
await fs6.appendFile(gitignorePath, "\n# CanopyCMS\n.canopy-dev/\n");
|
|
735
874
|
p.log.success("updated: .gitignore");
|
|
736
875
|
}
|
|
737
876
|
}
|
|
@@ -756,36 +895,80 @@ async function init(options) {
|
|
|
756
895
|
async function initDeployAws(options) {
|
|
757
896
|
const { projectDir, force, nonInteractive } = options;
|
|
758
897
|
const writeOpts = { force, nonInteractive };
|
|
759
|
-
const { dockerfileCms: dockerfileCms2, dockerignore: dockerignore2, githubWorkflowCms: githubWorkflowCms2 } = await Promise.resolve().then(() => (init_templates(), templates_exports));
|
|
898
|
+
const { dockerfileCms: dockerfileCms2, dockerignore: dockerignore2, githubWorkflowCms: githubWorkflowCms2, cdkJson: cdkJson2, cdkApp: cdkApp2, cmsStack: cmsStack2 } = await Promise.resolve().then(() => (init_templates(), templates_exports));
|
|
899
|
+
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));
|
|
760
900
|
p.intro("CanopyCMS init-deploy aws");
|
|
761
|
-
|
|
762
|
-
const
|
|
901
|
+
const packageManager = await detectPackageManager2(projectDir);
|
|
902
|
+
const pm = commandsFor2(packageManager);
|
|
903
|
+
const defaultBranch = await detectDefaultBranch2(projectDir);
|
|
904
|
+
const repo = await detectGitHubRepo2(projectDir);
|
|
905
|
+
await writeFile(path7.join(projectDir, "Dockerfile.cms"), await dockerfileCms2({ copy: pm.dockerCopy, install: pm.dockerInstall, build: pm.build }), writeOpts);
|
|
906
|
+
const dockerignoreWritten = await writeFile(path7.join(projectDir, ".dockerignore"), await dockerignore2(), writeOpts);
|
|
763
907
|
if (!dockerignoreWritten) {
|
|
764
|
-
p.log.warn("Existing .dockerignore kept \u2014 verify it excludes .env* (secrets would otherwise enter the build context) and does NOT exclude vendor/ (needed
|
|
908
|
+
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).");
|
|
765
909
|
}
|
|
766
|
-
await writeFile(
|
|
767
|
-
|
|
768
|
-
|
|
910
|
+
await writeFile(path7.join(projectDir, ".github/workflows/deploy-cms.yml"), await githubWorkflowCms2({
|
|
911
|
+
defaultBranch,
|
|
912
|
+
lockfile: pm.lockfile,
|
|
913
|
+
ciInstall: pm.ciInstall,
|
|
914
|
+
addDev: pm.addDev
|
|
915
|
+
}), writeOpts);
|
|
916
|
+
const cdkJsonPath = path7.join(projectDir, "cdk.json");
|
|
917
|
+
const existingCdkJson = await filePathExists2(cdkJsonPath) ? await fs6.readFile(cdkJsonPath, "utf-8") : null;
|
|
918
|
+
const cdkJsonWritten = await writeFile(cdkJsonPath, await cdkJson2(), writeOpts);
|
|
919
|
+
await writeFile(path7.join(projectDir, "infrastructure/bin/app.ts"), await cdkApp2({
|
|
920
|
+
githubOwner: repo?.owner ?? "your-org",
|
|
921
|
+
githubRepo: repo?.repo ?? "your-docs-site"
|
|
922
|
+
}), writeOpts);
|
|
923
|
+
await writeFile(path7.join(projectDir, "infrastructure/lib/cms-stack.ts"), await cmsStack2(), writeOpts);
|
|
924
|
+
if (!cdkJsonWritten && existingCdkJson && !existingCdkJson.includes("infrastructure/bin/app.ts")) {
|
|
925
|
+
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.");
|
|
926
|
+
}
|
|
927
|
+
const nextConfigPath = path7.join(projectDir, "next.config.ts");
|
|
928
|
+
const nextConfigMjsPath = path7.join(projectDir, "next.config.mjs");
|
|
769
929
|
const configPath = await filePathExists2(nextConfigPath) ? nextConfigPath : await filePathExists2(nextConfigMjsPath) ? nextConfigMjsPath : null;
|
|
770
930
|
if (configPath) {
|
|
771
|
-
const content = await
|
|
931
|
+
const content = await fs6.readFile(configPath, "utf-8");
|
|
772
932
|
if (!content.includes("CANOPY_BUILD")) {
|
|
773
933
|
p.note([
|
|
774
|
-
`Add dual build support to ${
|
|
934
|
+
`Add dual build support to ${path7.basename(configPath)}:`,
|
|
775
935
|
"",
|
|
776
936
|
" output: process.env.CANOPY_BUILD === 'cms' ? 'standalone' : 'export',"
|
|
777
937
|
].join("\n"), "Manual step");
|
|
778
938
|
}
|
|
779
939
|
}
|
|
780
|
-
|
|
940
|
+
if (packageManager === "yarn" || packageManager === "yarn-berry") {
|
|
941
|
+
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." : ""));
|
|
942
|
+
}
|
|
943
|
+
const missing = await missingCdkDependencies2(projectDir);
|
|
944
|
+
if (missing.length > 0) {
|
|
945
|
+
p.log.warn(`Not installed: ${missing.join(", ")}. cdk.json runs \`node --import tsx infrastructure/bin/app.ts\`, so the deploy fails without them:
|
|
946
|
+
${pm.addDev} ${CDK_DEPENDENCIES2.join(" ")}`);
|
|
947
|
+
}
|
|
948
|
+
p.note([
|
|
949
|
+
"1. Install the CDK dependencies (if you have not already):",
|
|
950
|
+
` ${pm.addDev} ${CDK_DEPENDENCIES2.join(" ")}`,
|
|
951
|
+
"",
|
|
952
|
+
"2. Fill in infrastructure/lib/cms-stack.ts and infrastructure/bin/app.ts",
|
|
953
|
+
` (worker repo currently set to ${repo ? `${repo.owner}/${repo.repo}` : "your-org/your-docs-site"})`,
|
|
954
|
+
"",
|
|
955
|
+
"3. Bootstrap CDK in the target account/region: cdk bootstrap",
|
|
956
|
+
"",
|
|
957
|
+
"4. Set the repository secrets and variables listed at the top of",
|
|
958
|
+
" .github/workflows/deploy-cms.yml \u2014 the deploy fails at synth without them.",
|
|
959
|
+
"",
|
|
960
|
+
`Deploys trigger on pushes to '${defaultBranch}'; edit the workflow to change that.`,
|
|
961
|
+
"",
|
|
962
|
+
"Full walkthrough: docs/deploying-to-aws.md"
|
|
963
|
+
].join("\n"), "AWS deployment");
|
|
781
964
|
p.outro("Done!");
|
|
782
965
|
}
|
|
783
966
|
async function detectMode(projectDir) {
|
|
784
|
-
const cfgPath =
|
|
967
|
+
const cfgPath = path7.join(projectDir, "canopycms.config.ts");
|
|
785
968
|
try {
|
|
786
|
-
await
|
|
969
|
+
await fs6.stat(cfgPath);
|
|
787
970
|
} catch (err) {
|
|
788
|
-
if (
|
|
971
|
+
if (isNotFoundError2(err))
|
|
789
972
|
return "dev";
|
|
790
973
|
throw err;
|
|
791
974
|
}
|
|
@@ -814,7 +997,7 @@ async function workerRunOnce(options) {
|
|
|
814
997
|
assertAuthPluginAllowedForMode(options.authPlugin, mode);
|
|
815
998
|
}
|
|
816
999
|
const taskDir = getTaskQueueDir2({ mode });
|
|
817
|
-
const cachePath = process.env.CANOPY_AUTH_CACHE_PATH ??
|
|
1000
|
+
const cachePath = process.env.CANOPY_AUTH_CACHE_PATH ?? path7.join(operatingStrategy(mode).getWorkspaceRoot(options.projectDir), ".cache");
|
|
818
1001
|
let refreshAuthCache;
|
|
819
1002
|
const authMode = process.env.CANOPY_AUTH_MODE || "dev";
|
|
820
1003
|
if (options.authPlugin?.createCacheRefresher) {
|
|
@@ -864,7 +1047,7 @@ CanopyCMS worker run-once (mode: ${mode}, auth: ${authMode})
|
|
|
864
1047
|
}
|
|
865
1048
|
console.log("\nDone");
|
|
866
1049
|
}
|
|
867
|
-
var __defProp2, __getOwnPropNames2, __esm2, __export2, primitiveFieldTypes, fieldTypes, init_types, fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, ASPECT_RATIO_RE, imageFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema, init_field, relativePathSchema, entryTypeSchema, collectionSchema, init_collection, mediaSchema, init_media, defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE, init_config, permissionTargetSchema, pathPermissionSchema, init_permissions, init_normalize, init_types2, init_flatten, init_validation, init_helpers, init_config2, templates_exports, __dirname, TEMPLATES_DIR, init_templates, task_queue_config_exports, init_task_queue_config, LOG_LEVELS, DebugLogger, testLogger, init_debug, init_atomic_write, DEFAULT_MAX_RETRIES, nullLogger, init_task_queue, init_task_queue2, task_queue_exports, debugLogger, cmsTaskQueueLogger, init_task_queue3, ProdClientSafeStrategy, DevClientSafeStrategy, ProdStrategy, DevStrategy, strategyCache;
|
|
1050
|
+
var __defProp2, __getOwnPropNames2, __esm2, __export2, primitiveFieldTypes, fieldTypes, init_types, fieldBaseSchema, selectOptionSchema, referenceOptionSchema, primitiveFieldSchema, ASPECT_RATIO_RE, imageFieldSchema, selectFieldSchema, referenceFieldSchema, fieldHolder, blockSchema, blockFieldSchema, objectFieldSchema, inlineGroupFieldSchema, customFieldSchema, knownFieldSchema, fieldSchema, init_field, relativePathSchema, entryTypeSchema, collectionSchema, init_collection, mediaSchema, init_media, defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE, init_config, permissionTargetSchema, pathPermissionSchema, init_permissions, init_normalize, init_types2, init_flatten, init_validation, init_helpers, init_config2, init_error2, init_fs2, templates_exports, __dirname, TEMPLATES_DIR, SCAFFOLD_STACK_NAME, init_templates, project_detect_exports, COMMANDS, MAX_WORKSPACE_DEPTH, CDK_DEPENDENCIES, init_project_detect, task_queue_config_exports, init_task_queue_config, LOG_LEVELS, DebugLogger, testLogger, init_debug, init_atomic_write, DEFAULT_MAX_RETRIES, nullLogger, init_task_queue, init_task_queue2, task_queue_exports, debugLogger, cmsTaskQueueLogger, init_task_queue3, ProdClientSafeStrategy, DevClientSafeStrategy, active, warned, VALID_DEPLOYMENT_NAME, isValidDeploymentName, ProdStrategy, DevStrategy, strategyCache;
|
|
868
1051
|
var init_init = __esm({
|
|
869
1052
|
"dist/cli/init.js"() {
|
|
870
1053
|
"use strict";
|
|
@@ -1116,6 +1299,16 @@ var init_init = __esm({
|
|
|
1116
1299
|
allowNetworkRemoteInProd: z4.boolean().optional(),
|
|
1117
1300
|
settingsBranch: z4.string().optional(),
|
|
1118
1301
|
autoCreateSettingsPR: z4.boolean().optional(),
|
|
1302
|
+
// Deliberately `deploymentNameSchema.optional()`, NOT `deploymentNameSchema` alone.
|
|
1303
|
+
// deploymentNameSchema.default('prod') would make `parse(undefined)` resolve to
|
|
1304
|
+
// the literal 'prod' instead of staying `undefined` — collapsing the
|
|
1305
|
+
// env > config > modeDefault precedence chain that resolveDeploymentName
|
|
1306
|
+
// (operating-mode/deployment-name.ts) implements: config would then always
|
|
1307
|
+
// "win" over modeDefault (masking dev's real default of 'local'), and the env
|
|
1308
|
+
// var would just be racing config's baked-in 'prod' instead of a true absence.
|
|
1309
|
+
// `.optional()` here is what keeps an omitted deploymentName reaching
|
|
1310
|
+
// resolveDeploymentName as `undefined`, so it can fall through to modeDefault.
|
|
1311
|
+
// Do not "fix" this by removing `.optional()`.
|
|
1119
1312
|
deploymentName: deploymentNameSchema.optional(),
|
|
1120
1313
|
contentRoot: contentRootSchema.default("content"),
|
|
1121
1314
|
sourceRoot: sourceRootSchema.optional(),
|
|
@@ -1187,13 +1380,28 @@ var init_init = __esm({
|
|
|
1187
1380
|
init_helpers();
|
|
1188
1381
|
}
|
|
1189
1382
|
});
|
|
1383
|
+
init_error2 = __esm2({
|
|
1384
|
+
"dist/utils/error.js"() {
|
|
1385
|
+
"use strict";
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
init_fs2 = __esm2({
|
|
1389
|
+
"dist/utils/fs.js"() {
|
|
1390
|
+
"use strict";
|
|
1391
|
+
init_error2();
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1190
1394
|
templates_exports = {};
|
|
1191
1395
|
__export2(templates_exports, {
|
|
1396
|
+
SCAFFOLD_STACK_NAME: () => SCAFFOLD_STACK_NAME,
|
|
1192
1397
|
aiConfig: () => aiConfig,
|
|
1193
1398
|
aiRoute: () => aiRoute,
|
|
1194
1399
|
apiRoute: () => apiRoute,
|
|
1195
1400
|
canopyCmsConfig: () => canopyCmsConfig,
|
|
1196
1401
|
canopyContext: () => canopyContext,
|
|
1402
|
+
cdkApp: () => cdkApp,
|
|
1403
|
+
cdkJson: () => cdkJson,
|
|
1404
|
+
cmsStack: () => cmsStack,
|
|
1197
1405
|
dockerfileCms: () => dockerfileCms,
|
|
1198
1406
|
dockerignore: () => dockerignore,
|
|
1199
1407
|
editPage: () => editPage,
|
|
@@ -1207,6 +1415,65 @@ var init_init = __esm({
|
|
|
1207
1415
|
"use strict";
|
|
1208
1416
|
__dirname = path2.dirname(fileURLToPath(import.meta.url));
|
|
1209
1417
|
TEMPLATES_DIR = path2.join(__dirname, "template-files");
|
|
1418
|
+
SCAFFOLD_STACK_NAME = "CanopyCms";
|
|
1419
|
+
}
|
|
1420
|
+
});
|
|
1421
|
+
project_detect_exports = {};
|
|
1422
|
+
__export2(project_detect_exports, {
|
|
1423
|
+
CDK_DEPENDENCIES: () => CDK_DEPENDENCIES,
|
|
1424
|
+
commandsFor: () => commandsFor,
|
|
1425
|
+
detectDefaultBranch: () => detectDefaultBranch,
|
|
1426
|
+
detectGitHubRepo: () => detectGitHubRepo,
|
|
1427
|
+
detectPackageManager: () => detectPackageManager,
|
|
1428
|
+
missingCdkDependencies: () => missingCdkDependencies,
|
|
1429
|
+
readPackageJson: () => readPackageJson
|
|
1430
|
+
});
|
|
1431
|
+
init_project_detect = __esm2({
|
|
1432
|
+
"dist/cli/project-detect.js"() {
|
|
1433
|
+
"use strict";
|
|
1434
|
+
init_fs2();
|
|
1435
|
+
COMMANDS = {
|
|
1436
|
+
npm: {
|
|
1437
|
+
lockfile: "package-lock.json",
|
|
1438
|
+
ciInstall: "npm ci",
|
|
1439
|
+
dockerCopy: "COPY package*.json ./",
|
|
1440
|
+
dockerInstall: "RUN npm ci",
|
|
1441
|
+
build: "npm run build",
|
|
1442
|
+
addDev: "npm install --save-dev"
|
|
1443
|
+
},
|
|
1444
|
+
pnpm: {
|
|
1445
|
+
lockfile: "pnpm-lock.yaml",
|
|
1446
|
+
ciInstall: "corepack enable && pnpm install --frozen-lockfile",
|
|
1447
|
+
dockerCopy: "COPY package.json pnpm-lock.yaml ./",
|
|
1448
|
+
dockerInstall: "RUN corepack enable && pnpm install --frozen-lockfile",
|
|
1449
|
+
build: "pnpm run build",
|
|
1450
|
+
addDev: "pnpm add -D"
|
|
1451
|
+
},
|
|
1452
|
+
yarn: {
|
|
1453
|
+
lockfile: "yarn.lock",
|
|
1454
|
+
ciInstall: "corepack enable && yarn install --frozen-lockfile",
|
|
1455
|
+
dockerCopy: "COPY package.json yarn.lock ./",
|
|
1456
|
+
dockerInstall: "RUN corepack enable && yarn install --frozen-lockfile",
|
|
1457
|
+
build: "yarn build",
|
|
1458
|
+
addDev: "yarn add -D"
|
|
1459
|
+
},
|
|
1460
|
+
"yarn-berry": {
|
|
1461
|
+
lockfile: "yarn.lock",
|
|
1462
|
+
ciInstall: "corepack enable && yarn install --immutable",
|
|
1463
|
+
dockerCopy: "COPY package.json yarn.lock .yarnrc.yml ./",
|
|
1464
|
+
dockerInstall: "RUN corepack enable && yarn install --immutable",
|
|
1465
|
+
build: "yarn build",
|
|
1466
|
+
addDev: "yarn add -D"
|
|
1467
|
+
}
|
|
1468
|
+
};
|
|
1469
|
+
MAX_WORKSPACE_DEPTH = 8;
|
|
1470
|
+
CDK_DEPENDENCIES = [
|
|
1471
|
+
"canopycms-cdk",
|
|
1472
|
+
"aws-cdk-lib",
|
|
1473
|
+
"constructs",
|
|
1474
|
+
"tsx",
|
|
1475
|
+
"aws-cdk"
|
|
1476
|
+
];
|
|
1210
1477
|
}
|
|
1211
1478
|
});
|
|
1212
1479
|
task_queue_config_exports = {};
|
|
@@ -1240,10 +1507,21 @@ var init_init = __esm({
|
|
|
1240
1507
|
const minLevel = this.options.minLevel ?? "DEBUG";
|
|
1241
1508
|
return LOG_LEVELS[level] >= LOG_LEVELS[minLevel];
|
|
1242
1509
|
}
|
|
1510
|
+
/**
|
|
1511
|
+
* The ISO-8601 timestamp leads the line BARE - not wrapped in brackets as it
|
|
1512
|
+
* once was. `CANOPYCMS_DEBUG=true` can be set on the EC2 worker, whose stdout
|
|
1513
|
+
* appends to /var/log/canopy-worker/worker.log; the CloudWatch agent's
|
|
1514
|
+
* `multi_line_start_pattern` matches the timestamp at the START of a line
|
|
1515
|
+
* (see worker/log.ts's INVARIANT and cms-service.ts's agent config), so a
|
|
1516
|
+
* leading `[` meant every debug line was silently folded into the previous
|
|
1517
|
+
* event instead of starting its own. The bracketed `[prefix:category]` and
|
|
1518
|
+
* `[level]` fields still follow, so the human-readable shape is unchanged
|
|
1519
|
+
* apart from those two characters.
|
|
1520
|
+
*/
|
|
1243
1521
|
formatMessage(level, category, message) {
|
|
1244
1522
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1245
1523
|
const prefix = this.options.prefix ?? "CanopyCMS";
|
|
1246
|
-
return
|
|
1524
|
+
return `${timestamp} [${prefix}:${category}] [${level}] ${message}`;
|
|
1247
1525
|
}
|
|
1248
1526
|
debug(category, message, data) {
|
|
1249
1527
|
if (this.shouldLog("DEBUG")) {
|
|
@@ -1422,6 +1700,10 @@ var init_init = __esm({
|
|
|
1422
1700
|
return true;
|
|
1423
1701
|
}
|
|
1424
1702
|
};
|
|
1703
|
+
active = console;
|
|
1704
|
+
warned = false;
|
|
1705
|
+
VALID_DEPLOYMENT_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
1706
|
+
isValidDeploymentName = (name) => VALID_DEPLOYMENT_NAME.test(name) && !name.includes("..") && !name.endsWith(".") && !name.endsWith(".lock");
|
|
1425
1707
|
init_config2();
|
|
1426
1708
|
ProdStrategy = class extends ProdClientSafeStrategy {
|
|
1427
1709
|
// All client-safe methods inherited automatically from ProdClientSafeStrategy:
|
|
@@ -1430,32 +1712,32 @@ var init_init = __esm({
|
|
|
1430
1712
|
// - shouldCommit(), shouldPush()
|
|
1431
1713
|
// Add client-unsafe methods (use Node.js APIs)
|
|
1432
1714
|
getWorkspaceRoot(_sourceRoot) {
|
|
1433
|
-
return
|
|
1715
|
+
return path8.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE);
|
|
1434
1716
|
}
|
|
1435
|
-
getContentRoot(sourceRoot) {
|
|
1436
|
-
return
|
|
1717
|
+
getContentRoot(contentRoot, sourceRoot) {
|
|
1718
|
+
return path8.resolve(sourceRoot ?? process.cwd(), contentRoot);
|
|
1437
1719
|
}
|
|
1438
1720
|
getContentBranchesRoot(sourceRoot) {
|
|
1439
|
-
return
|
|
1721
|
+
return path8.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
|
|
1440
1722
|
}
|
|
1441
1723
|
getContentBranchRoot(branchName, sourceRoot) {
|
|
1442
|
-
return
|
|
1724
|
+
return path8.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
|
|
1443
1725
|
}
|
|
1444
1726
|
getGitExcludePattern() {
|
|
1445
1727
|
return ".canopy-meta/";
|
|
1446
1728
|
}
|
|
1447
1729
|
getPermissionsFilePath(root) {
|
|
1448
|
-
return
|
|
1730
|
+
return path8.join(root, this.getPermissionsFileName());
|
|
1449
1731
|
}
|
|
1450
1732
|
getGroupsFilePath(root) {
|
|
1451
|
-
return
|
|
1733
|
+
return path8.join(root, this.getGroupsFileName());
|
|
1452
1734
|
}
|
|
1453
1735
|
getRemoteUrlConfig() {
|
|
1454
1736
|
return {
|
|
1455
1737
|
shouldAutoInitLocal: false,
|
|
1456
1738
|
defaultRemotePath: "",
|
|
1457
1739
|
envVarName: "CANOPYCMS_REMOTE_URL",
|
|
1458
|
-
autoDetectRemotePath:
|
|
1740
|
+
autoDetectRemotePath: path8.join(this.getWorkspaceRoot(), "remote.git")
|
|
1459
1741
|
};
|
|
1460
1742
|
}
|
|
1461
1743
|
requiresExistingRepo() {
|
|
@@ -1464,11 +1746,10 @@ var init_init = __esm({
|
|
|
1464
1746
|
getSettingsBranchName(config) {
|
|
1465
1747
|
if (config.settingsBranch)
|
|
1466
1748
|
return config.settingsBranch;
|
|
1467
|
-
|
|
1468
|
-
return `canopycms-settings-${deploymentName}`;
|
|
1749
|
+
return `canopycms-settings-${resolveDeploymentName(config, "prod")}`;
|
|
1469
1750
|
}
|
|
1470
1751
|
getSettingsRoot(sourceRoot) {
|
|
1471
|
-
return
|
|
1752
|
+
return path8.join(this.getWorkspaceRoot(sourceRoot), "settings");
|
|
1472
1753
|
}
|
|
1473
1754
|
usesSeparateSettingsBranch() {
|
|
1474
1755
|
return true;
|
|
@@ -1485,25 +1766,25 @@ var init_init = __esm({
|
|
|
1485
1766
|
DevStrategy = class extends DevClientSafeStrategy {
|
|
1486
1767
|
// Inherits client-safe methods from DevClientSafeStrategy
|
|
1487
1768
|
getWorkspaceRoot(sourceRoot) {
|
|
1488
|
-
return
|
|
1769
|
+
return path8.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
|
|
1489
1770
|
}
|
|
1490
|
-
getContentRoot(sourceRoot) {
|
|
1491
|
-
return
|
|
1771
|
+
getContentRoot(contentRoot, sourceRoot) {
|
|
1772
|
+
return path8.resolve(sourceRoot ?? process.cwd(), contentRoot);
|
|
1492
1773
|
}
|
|
1493
1774
|
getContentBranchesRoot(sourceRoot) {
|
|
1494
|
-
return
|
|
1775
|
+
return path8.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
|
|
1495
1776
|
}
|
|
1496
1777
|
getContentBranchRoot(branchName, sourceRoot) {
|
|
1497
|
-
return
|
|
1778
|
+
return path8.resolve(this.getContentBranchesRoot(sourceRoot), branchName);
|
|
1498
1779
|
}
|
|
1499
1780
|
getGitExcludePattern() {
|
|
1500
1781
|
return ".canopy-meta/";
|
|
1501
1782
|
}
|
|
1502
1783
|
getPermissionsFilePath(root) {
|
|
1503
|
-
return
|
|
1784
|
+
return path8.join(root, this.getPermissionsFileName());
|
|
1504
1785
|
}
|
|
1505
1786
|
getGroupsFilePath(root) {
|
|
1506
|
-
return
|
|
1787
|
+
return path8.join(root, this.getGroupsFileName());
|
|
1507
1788
|
}
|
|
1508
1789
|
getRemoteUrlConfig() {
|
|
1509
1790
|
return {
|
|
@@ -1518,11 +1799,10 @@ var init_init = __esm({
|
|
|
1518
1799
|
getSettingsBranchName(config) {
|
|
1519
1800
|
if (config.settingsBranch)
|
|
1520
1801
|
return config.settingsBranch;
|
|
1521
|
-
|
|
1522
|
-
return `canopycms-settings-${deploymentName}`;
|
|
1802
|
+
return `canopycms-settings-${resolveDeploymentName(config, "local")}`;
|
|
1523
1803
|
}
|
|
1524
1804
|
getSettingsRoot(sourceRoot) {
|
|
1525
|
-
return
|
|
1805
|
+
return path8.join(this.getWorkspaceRoot(sourceRoot), "settings");
|
|
1526
1806
|
}
|
|
1527
1807
|
usesSeparateSettingsBranch() {
|
|
1528
1808
|
return true;
|
|
@@ -1534,6 +1814,8 @@ var init_init = __esm({
|
|
|
1534
1814
|
}
|
|
1535
1815
|
};
|
|
1536
1816
|
strategyCache = /* @__PURE__ */ new Map();
|
|
1817
|
+
init_fs2();
|
|
1818
|
+
init_error2();
|
|
1537
1819
|
}
|
|
1538
1820
|
});
|
|
1539
1821
|
|
|
@@ -1555,12 +1837,12 @@ import { createJiti as createJiti2 } from "jiti";
|
|
|
1555
1837
|
import fs16 from "node:fs/promises";
|
|
1556
1838
|
import path17 from "node:path";
|
|
1557
1839
|
import fs8 from "node:fs/promises";
|
|
1558
|
-
import
|
|
1840
|
+
import path82 from "node:path";
|
|
1559
1841
|
import matter from "gray-matter";
|
|
1560
1842
|
import { parse as yamlParse, stringify as yamlStringify } from "yaml";
|
|
1561
1843
|
import fs7 from "node:fs/promises";
|
|
1562
1844
|
import path9 from "node:path";
|
|
1563
|
-
import
|
|
1845
|
+
import fs23 from "node:fs/promises";
|
|
1564
1846
|
import path22 from "node:path";
|
|
1565
1847
|
import { generate } from "short-uuid";
|
|
1566
1848
|
import path32 from "node:path";
|
|
@@ -1592,7 +1874,7 @@ import lockfile from "proper-lockfile";
|
|
|
1592
1874
|
import fs14 from "node:fs/promises";
|
|
1593
1875
|
import path14 from "node:path";
|
|
1594
1876
|
import { simpleGit as simpleGit2 } from "simple-git";
|
|
1595
|
-
import { simpleGit } from "simple-git";
|
|
1877
|
+
import { simpleGit as simpleGit3 } from "simple-git";
|
|
1596
1878
|
import fs13 from "node:fs/promises";
|
|
1597
1879
|
import path13 from "node:path";
|
|
1598
1880
|
import lockfile2 from "proper-lockfile";
|
|
@@ -1623,6 +1905,12 @@ function trimSlashes(path192) {
|
|
|
1623
1905
|
end--;
|
|
1624
1906
|
return path192.slice(start, end);
|
|
1625
1907
|
}
|
|
1908
|
+
function canopyLogWarn2(...args) {
|
|
1909
|
+
active2.warn(...args);
|
|
1910
|
+
}
|
|
1911
|
+
function canopyLogError(...args) {
|
|
1912
|
+
active2.error(...args);
|
|
1913
|
+
}
|
|
1626
1914
|
function clientOperatingStrategy(mode) {
|
|
1627
1915
|
const cached = clientStrategyCache.get(mode);
|
|
1628
1916
|
if (cached)
|
|
@@ -1646,6 +1934,20 @@ function clientOperatingStrategy(mode) {
|
|
|
1646
1934
|
function clearClientStrategyCache() {
|
|
1647
1935
|
clientStrategyCache.clear();
|
|
1648
1936
|
}
|
|
1937
|
+
function resolveDeploymentName2(config, modeDefault) {
|
|
1938
|
+
const envValue = process.env.CANOPYCMS_DEPLOYMENT_NAME?.trim();
|
|
1939
|
+
const configValue = config.deploymentName;
|
|
1940
|
+
if (envValue && configValue && envValue !== configValue && !warned2) {
|
|
1941
|
+
canopyLogWarn2(`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.`);
|
|
1942
|
+
warned2 = true;
|
|
1943
|
+
}
|
|
1944
|
+
const resolved = envValue || configValue || modeDefault;
|
|
1945
|
+
if (!isValidDeploymentName2(resolved)) {
|
|
1946
|
+
const source = envValue ? "CANOPYCMS_DEPLOYMENT_NAME" : configValue ? "config.deploymentName" : "the mode default";
|
|
1947
|
+
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'.`);
|
|
1948
|
+
}
|
|
1949
|
+
return resolved;
|
|
1950
|
+
}
|
|
1649
1951
|
function operatingStrategy2(mode) {
|
|
1650
1952
|
const cached = strategyCache2.get(mode);
|
|
1651
1953
|
if (cached)
|
|
@@ -1735,7 +2037,7 @@ function getErrorMessage3(err) {
|
|
|
1735
2037
|
function isNodeError3(err) {
|
|
1736
2038
|
return err instanceof Error && "code" in err;
|
|
1737
2039
|
}
|
|
1738
|
-
function
|
|
2040
|
+
function isNotFoundError3(err) {
|
|
1739
2041
|
return isNodeError3(err) && err.code === "ENOENT";
|
|
1740
2042
|
}
|
|
1741
2043
|
function isFileExistsError2(err) {
|
|
@@ -1783,7 +2085,7 @@ async function resolveCollectionPath(root, logicalPath) {
|
|
|
1783
2085
|
return null;
|
|
1784
2086
|
}
|
|
1785
2087
|
} catch (err) {
|
|
1786
|
-
if (
|
|
2088
|
+
if (isNotFoundError3(err))
|
|
1787
2089
|
return null;
|
|
1788
2090
|
throw err;
|
|
1789
2091
|
}
|
|
@@ -1923,7 +2225,7 @@ async function scanForCollectionMeta(baseDir, relativePath = "") {
|
|
|
1923
2225
|
collections.push(...nestedCollections);
|
|
1924
2226
|
} catch (err) {
|
|
1925
2227
|
if (err.code !== "ENOENT") {
|
|
1926
|
-
|
|
2228
|
+
canopyLogError(`Error loading ${metaPath}:`, err);
|
|
1927
2229
|
throw new Error(`Invalid .collection.json in ${folderPath}: ${err.message}`);
|
|
1928
2230
|
}
|
|
1929
2231
|
const nestedCollections = await scanForCollectionMeta(absolutePath, folderPath);
|
|
@@ -2075,7 +2377,7 @@ async function filePathExists3(filePath) {
|
|
|
2075
2377
|
await fs62.stat(filePath);
|
|
2076
2378
|
return true;
|
|
2077
2379
|
} catch (err) {
|
|
2078
|
-
if (
|
|
2380
|
+
if (isNotFoundError3(err))
|
|
2079
2381
|
return false;
|
|
2080
2382
|
throw err;
|
|
2081
2383
|
}
|
|
@@ -3149,7 +3451,7 @@ function isNetworkRemoteUrl(url) {
|
|
|
3149
3451
|
}
|
|
3150
3452
|
async function detectHeadBranch(repoRoot, fallback = "main") {
|
|
3151
3453
|
try {
|
|
3152
|
-
const git =
|
|
3454
|
+
const git = simpleGit3({ baseDir: repoRoot });
|
|
3153
3455
|
const head = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
|
|
3154
3456
|
return head && head !== "HEAD" ? head : fallback;
|
|
3155
3457
|
} catch {
|
|
@@ -3178,7 +3480,7 @@ function gitChildEnv(overrides) {
|
|
|
3178
3480
|
if (value !== void 0 && GIT_ENV_PASSTHROUGH.test(key))
|
|
3179
3481
|
env[key] = value;
|
|
3180
3482
|
}
|
|
3181
|
-
return { ...env, ...overrides };
|
|
3483
|
+
return { ...env, ...FORCE_C_LOCALE, ...overrides };
|
|
3182
3484
|
}
|
|
3183
3485
|
async function ensureGitExcludePattern(repoPath, pattern) {
|
|
3184
3486
|
const excludePath = path14.join(repoPath, ".git", "info", "exclude");
|
|
@@ -3187,7 +3489,7 @@ async function ensureGitExcludePattern(repoPath, pattern) {
|
|
|
3187
3489
|
try {
|
|
3188
3490
|
content = await fs14.readFile(excludePath, "utf-8");
|
|
3189
3491
|
} catch (err) {
|
|
3190
|
-
if (!
|
|
3492
|
+
if (!isNotFoundError3(err))
|
|
3191
3493
|
throw err;
|
|
3192
3494
|
}
|
|
3193
3495
|
const lines = content.split("\n");
|
|
@@ -3567,7 +3869,7 @@ async function generateAIContentFiles(options) {
|
|
|
3567
3869
|
}
|
|
3568
3870
|
const entriesForValidation = await listEntries(branchRoot, flatSchema, contentRootName);
|
|
3569
3871
|
assertBuildEntriesValid(entriesForValidation, "AI content generation");
|
|
3570
|
-
const store = new ContentStore(branchRoot, flatSchema);
|
|
3872
|
+
const store = new ContentStore(branchRoot, flatSchema, { contentRootName });
|
|
3571
3873
|
const result = await generateAIContent({
|
|
3572
3874
|
store,
|
|
3573
3875
|
flatSchema,
|
|
@@ -3644,7 +3946,7 @@ async function generateAIContentCLI(options) {
|
|
|
3644
3946
|
console.log(` Output: ${result.outputDir}
|
|
3645
3947
|
`);
|
|
3646
3948
|
}
|
|
3647
|
-
var __defProp3, __getOwnPropNames3, __esm3, __export3, init_normalize2, ROOT_COLLECTION_ID, init_types3, normalizePathValue, flattenSchema, init_flatten2,
|
|
3949
|
+
var __defProp3, __getOwnPropNames3, __esm3, __export3, init_normalize2, active2, init_logger, ROOT_COLLECTION_ID, init_types3, normalizePathValue, flattenSchema, init_flatten2, primitiveFieldTypes2, fieldTypes2, init_types22, fieldBaseSchema2, selectOptionSchema2, referenceOptionSchema2, primitiveFieldSchema2, ASPECT_RATIO_RE2, imageFieldSchema2, selectFieldSchema2, referenceFieldSchema2, fieldHolder2, blockSchema2, blockFieldSchema2, objectFieldSchema2, inlineGroupFieldSchema2, customFieldSchema2, knownFieldSchema2, fieldSchema2, init_field2, relativePathSchema2, entryTypeSchema2, collectionSchema2, init_collection2, mediaSchema2, init_media2, defaultBranchAccessSchema2, defaultPathAccessLevelSchema2, defaultPathAccessLevelsSchema2, defaultPathAccessSchema2, defaultBaseBranchSchema2, defaultRemoteNameSchema2, defaultRemoteUrlSchema2, gitBotAuthorNameSchema2, gitBotAuthorEmailSchema2, githubTokenEnvVarSchema2, operatingModeSchema2, deployedAsSchema2, contentRootSchema2, sourceRootSchema2, deploymentNameSchema2, devContentSyncSchema2, devConfigSchema2, editorConfigSchema2, CanopyConfigSchema2, DEFAULT_PROD_WORKSPACE2, init_config3, forEachReferenceField, init_validation2, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned2, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, permissionTargetSchema2, pathPermissionSchema2, init_permissions2, init_helpers2, init_config22, ProdStrategy2, DevStrategy2, strategyCache2, init_client_unsafe_strategy, operating_mode_exports, init_operating_mode, locks, BASE58_PATTERN, CONTENT_ID_PATTERN, PHYSICAL_SEGMENT_PATTERN, isValidId, EMPTY_LOGICAL_PATH, ContentIdIndex, registry, finalization, LOG_LEVELS2, DebugLogger2, testLogger2, log2, META_DIR, entryTypeMetaSchema, collectionMetaSchema, rootCollectionMetaSchema, editDistance, closestName, forEachEntryType, collectEntryTypeNames, validateReferenceEntryTypes, isDeployedStatic, isBuildMode, STATIC_DEPLOY_USER, SCHEMA_CACHE_VERSION, MTIME_CHECK_DEBOUNCE_MS, SCHEMA_GENERATION_RESOURCE, BranchSchemaCache, RESOURCE, getFormatExtension, isDataOnlyFormat, asRecord, BranchPathError, resolveContentBranchesRoot, ContentStoreError, ContentConflictError, DEFAULT_INDEX_FRESHNESS_INTERVAL_MS, FORCED_REFRESH_MIN_INTERVAL_MS, STALE_LOOKUP, ContentStore, ATTR_CONTENT, BLOCK_PREFIX, BLOCK_SUFFIX, BLOCK_RESTORE_RE, INLINE_PREFIX, INLINE_SUFFIX, INLINE_RESTORE_RE, FLAT_CELL_TYPES, log22, BASE58_CHAR, ENTRY_LINK_PATTERN, log3, REGISTRY_FILE, REGISTRY_STALE_FILE, REGISTRY_TEMP_FILE, REGISTRY_VERSION, RESOURCE2, GET_MISS_REFRESH_MIN_INTERVAL_MS, BranchRegistry, log4, OccWriteConflictError, BRANCH_META_DIR, BRANCH_META_FILE, CURRENT_SCHEMA_VERSION, BranchMetadataConflictError, BranchMetadataCorruptError, BranchMetadataFileManager, getBranchMetadataFileManager, loadBranchContext, NETWORK_SCHEME_PATTERN, TRANSPORT_HELPER_PATTERN, LEADING_OPTION_PATTERN, WINDOWS_DRIVE_PATTERN, COLON_BEFORE_SLASH_PATTERN, log5, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, GitConflictError, GitManager, log6, workspaceInitLocks, BranchWorkspaceManager, log7, normalizePath, readEntryData, parseTypedFilename, listCollectionEntries, MAX_INPUT_PIXELS, KNOWN_FIELD_TYPES, STRING_FIELD_TYPES, selectOptionValues, jiti;
|
|
3648
3950
|
var init_generate_ai_content = __esm({
|
|
3649
3951
|
"dist/cli/generate-ai-content.js"() {
|
|
3650
3952
|
"use strict";
|
|
@@ -3662,6 +3964,12 @@ var init_generate_ai_content = __esm({
|
|
|
3662
3964
|
"use strict";
|
|
3663
3965
|
}
|
|
3664
3966
|
});
|
|
3967
|
+
init_logger = __esm3({
|
|
3968
|
+
"dist/utils/logger.js"() {
|
|
3969
|
+
"use strict";
|
|
3970
|
+
active2 = console;
|
|
3971
|
+
}
|
|
3972
|
+
});
|
|
3665
3973
|
init_types3 = __esm3({
|
|
3666
3974
|
"dist/paths/types.js"() {
|
|
3667
3975
|
"use strict";
|
|
@@ -3767,74 +4075,6 @@ var init_generate_ai_content = __esm({
|
|
|
3767
4075
|
};
|
|
3768
4076
|
}
|
|
3769
4077
|
});
|
|
3770
|
-
init_client_safe_strategy = __esm3({
|
|
3771
|
-
"dist/operating-mode/client-safe-strategy.js"() {
|
|
3772
|
-
"use strict";
|
|
3773
|
-
ProdClientSafeStrategy2 = class {
|
|
3774
|
-
constructor() {
|
|
3775
|
-
this.mode = "prod";
|
|
3776
|
-
}
|
|
3777
|
-
// UI Feature Flags
|
|
3778
|
-
supportsBranching() {
|
|
3779
|
-
return true;
|
|
3780
|
-
}
|
|
3781
|
-
supportsStatusBadge() {
|
|
3782
|
-
return true;
|
|
3783
|
-
}
|
|
3784
|
-
supportsComments() {
|
|
3785
|
-
return true;
|
|
3786
|
-
}
|
|
3787
|
-
supportsPullRequests() {
|
|
3788
|
-
return true;
|
|
3789
|
-
}
|
|
3790
|
-
// Simple Data
|
|
3791
|
-
getPermissionsFileName() {
|
|
3792
|
-
return "permissions.json";
|
|
3793
|
-
}
|
|
3794
|
-
getGroupsFileName() {
|
|
3795
|
-
return "groups.json";
|
|
3796
|
-
}
|
|
3797
|
-
shouldCommit() {
|
|
3798
|
-
return true;
|
|
3799
|
-
}
|
|
3800
|
-
shouldPush() {
|
|
3801
|
-
return true;
|
|
3802
|
-
}
|
|
3803
|
-
};
|
|
3804
|
-
DevClientSafeStrategy2 = class {
|
|
3805
|
-
constructor() {
|
|
3806
|
-
this.mode = "dev";
|
|
3807
|
-
}
|
|
3808
|
-
// UI Feature Flags
|
|
3809
|
-
supportsBranching() {
|
|
3810
|
-
return true;
|
|
3811
|
-
}
|
|
3812
|
-
supportsStatusBadge() {
|
|
3813
|
-
return true;
|
|
3814
|
-
}
|
|
3815
|
-
supportsComments() {
|
|
3816
|
-
return true;
|
|
3817
|
-
}
|
|
3818
|
-
supportsPullRequests() {
|
|
3819
|
-
return false;
|
|
3820
|
-
}
|
|
3821
|
-
// Simple Data
|
|
3822
|
-
getPermissionsFileName() {
|
|
3823
|
-
return "permissions.json";
|
|
3824
|
-
}
|
|
3825
|
-
getGroupsFileName() {
|
|
3826
|
-
return "groups.json";
|
|
3827
|
-
}
|
|
3828
|
-
shouldCommit() {
|
|
3829
|
-
return true;
|
|
3830
|
-
}
|
|
3831
|
-
shouldPush() {
|
|
3832
|
-
return true;
|
|
3833
|
-
}
|
|
3834
|
-
};
|
|
3835
|
-
clientStrategyCache = /* @__PURE__ */ new Map();
|
|
3836
|
-
}
|
|
3837
|
-
});
|
|
3838
4078
|
init_types22 = __esm3({
|
|
3839
4079
|
"dist/config/types.js"() {
|
|
3840
4080
|
"use strict";
|
|
@@ -4074,6 +4314,16 @@ var init_generate_ai_content = __esm({
|
|
|
4074
4314
|
allowNetworkRemoteInProd: z52.boolean().optional(),
|
|
4075
4315
|
settingsBranch: z52.string().optional(),
|
|
4076
4316
|
autoCreateSettingsPR: z52.boolean().optional(),
|
|
4317
|
+
// Deliberately `deploymentNameSchema.optional()`, NOT `deploymentNameSchema` alone.
|
|
4318
|
+
// deploymentNameSchema.default('prod') would make `parse(undefined)` resolve to
|
|
4319
|
+
// the literal 'prod' instead of staying `undefined` — collapsing the
|
|
4320
|
+
// env > config > modeDefault precedence chain that resolveDeploymentName
|
|
4321
|
+
// (operating-mode/deployment-name.ts) implements: config would then always
|
|
4322
|
+
// "win" over modeDefault (masking dev's real default of 'local'), and the env
|
|
4323
|
+
// var would just be racing config's baked-in 'prod' instead of a true absence.
|
|
4324
|
+
// `.optional()` here is what keeps an omitted deploymentName reaching
|
|
4325
|
+
// resolveDeploymentName as `undefined`, so it can fall through to modeDefault.
|
|
4326
|
+
// Do not "fix" this by removing `.optional()`.
|
|
4077
4327
|
deploymentName: deploymentNameSchema2.optional(),
|
|
4078
4328
|
contentRoot: contentRootSchema2.default("content"),
|
|
4079
4329
|
sourceRoot: sourceRootSchema2.optional(),
|
|
@@ -4086,42 +4336,137 @@ var init_generate_ai_content = __esm({
|
|
|
4086
4336
|
DEFAULT_PROD_WORKSPACE2 = "/mnt/efs/workspace";
|
|
4087
4337
|
}
|
|
4088
4338
|
});
|
|
4089
|
-
init_permissions2 = __esm3({
|
|
4090
|
-
"dist/config/schemas/permissions.js"() {
|
|
4091
|
-
"use strict";
|
|
4092
|
-
permissionTargetSchema2 = z6.object({
|
|
4093
|
-
allowedUsers: z6.array(z6.string()).optional(),
|
|
4094
|
-
allowedGroups: z6.array(z6.string()).optional()
|
|
4095
|
-
});
|
|
4096
|
-
pathPermissionSchema2 = z6.object({
|
|
4097
|
-
path: z6.string().min(1),
|
|
4098
|
-
read: permissionTargetSchema2.optional(),
|
|
4099
|
-
edit: permissionTargetSchema2.optional(),
|
|
4100
|
-
review: permissionTargetSchema2.optional()
|
|
4101
|
-
});
|
|
4102
|
-
}
|
|
4103
|
-
});
|
|
4104
4339
|
init_validation2 = __esm3({
|
|
4105
4340
|
"dist/config/validation.js"() {
|
|
4106
4341
|
"use strict";
|
|
4107
4342
|
init_config3();
|
|
4108
4343
|
init_flatten2();
|
|
4344
|
+
forEachReferenceField = (fields, visit) => {
|
|
4345
|
+
if (!Array.isArray(fields))
|
|
4346
|
+
return;
|
|
4347
|
+
for (const field of fields) {
|
|
4348
|
+
const f = field;
|
|
4349
|
+
if (f?.type === "reference") {
|
|
4350
|
+
visit(f);
|
|
4351
|
+
}
|
|
4352
|
+
if (f?.type === "group" || f?.type === "object") {
|
|
4353
|
+
forEachReferenceField(f.fields, visit);
|
|
4354
|
+
}
|
|
4355
|
+
if (f?.type === "block" && Array.isArray(f.templates)) {
|
|
4356
|
+
for (const template of f.templates) {
|
|
4357
|
+
forEachReferenceField(template.fields, visit);
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
};
|
|
4109
4362
|
}
|
|
4110
4363
|
});
|
|
4111
|
-
|
|
4112
|
-
"dist/
|
|
4113
|
-
"use strict";
|
|
4114
|
-
init_validation2();
|
|
4115
|
-
}
|
|
4116
|
-
});
|
|
4117
|
-
init_config22 = __esm3({
|
|
4118
|
-
"dist/config/index.js"() {
|
|
4364
|
+
init_client_safe_strategy = __esm3({
|
|
4365
|
+
"dist/operating-mode/client-safe-strategy.js"() {
|
|
4119
4366
|
"use strict";
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4367
|
+
ProdClientSafeStrategy2 = class {
|
|
4368
|
+
constructor() {
|
|
4369
|
+
this.mode = "prod";
|
|
4370
|
+
}
|
|
4371
|
+
// UI Feature Flags
|
|
4372
|
+
supportsBranching() {
|
|
4373
|
+
return true;
|
|
4374
|
+
}
|
|
4375
|
+
supportsStatusBadge() {
|
|
4376
|
+
return true;
|
|
4377
|
+
}
|
|
4378
|
+
supportsComments() {
|
|
4379
|
+
return true;
|
|
4380
|
+
}
|
|
4381
|
+
supportsPullRequests() {
|
|
4382
|
+
return true;
|
|
4383
|
+
}
|
|
4384
|
+
// Simple Data
|
|
4385
|
+
getPermissionsFileName() {
|
|
4386
|
+
return "permissions.json";
|
|
4387
|
+
}
|
|
4388
|
+
getGroupsFileName() {
|
|
4389
|
+
return "groups.json";
|
|
4390
|
+
}
|
|
4391
|
+
shouldCommit() {
|
|
4392
|
+
return true;
|
|
4393
|
+
}
|
|
4394
|
+
shouldPush() {
|
|
4395
|
+
return true;
|
|
4396
|
+
}
|
|
4397
|
+
};
|
|
4398
|
+
DevClientSafeStrategy2 = class {
|
|
4399
|
+
constructor() {
|
|
4400
|
+
this.mode = "dev";
|
|
4401
|
+
}
|
|
4402
|
+
// UI Feature Flags
|
|
4403
|
+
supportsBranching() {
|
|
4404
|
+
return true;
|
|
4405
|
+
}
|
|
4406
|
+
supportsStatusBadge() {
|
|
4407
|
+
return true;
|
|
4408
|
+
}
|
|
4409
|
+
supportsComments() {
|
|
4410
|
+
return true;
|
|
4411
|
+
}
|
|
4412
|
+
supportsPullRequests() {
|
|
4413
|
+
return false;
|
|
4414
|
+
}
|
|
4415
|
+
// Simple Data
|
|
4416
|
+
getPermissionsFileName() {
|
|
4417
|
+
return "permissions.json";
|
|
4418
|
+
}
|
|
4419
|
+
getGroupsFileName() {
|
|
4420
|
+
return "groups.json";
|
|
4421
|
+
}
|
|
4422
|
+
shouldCommit() {
|
|
4423
|
+
return true;
|
|
4424
|
+
}
|
|
4425
|
+
shouldPush() {
|
|
4426
|
+
return true;
|
|
4427
|
+
}
|
|
4428
|
+
};
|
|
4429
|
+
clientStrategyCache = /* @__PURE__ */ new Map();
|
|
4430
|
+
}
|
|
4431
|
+
});
|
|
4432
|
+
init_deployment_name = __esm3({
|
|
4433
|
+
"dist/operating-mode/deployment-name.js"() {
|
|
4434
|
+
"use strict";
|
|
4435
|
+
init_logger();
|
|
4436
|
+
warned2 = false;
|
|
4437
|
+
VALID_DEPLOYMENT_NAME2 = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
4438
|
+
isValidDeploymentName2 = (name) => VALID_DEPLOYMENT_NAME2.test(name) && !name.includes("..") && !name.endsWith(".") && !name.endsWith(".lock");
|
|
4439
|
+
}
|
|
4440
|
+
});
|
|
4441
|
+
init_permissions2 = __esm3({
|
|
4442
|
+
"dist/config/schemas/permissions.js"() {
|
|
4443
|
+
"use strict";
|
|
4444
|
+
permissionTargetSchema2 = z6.object({
|
|
4445
|
+
allowedUsers: z6.array(z6.string()).optional(),
|
|
4446
|
+
allowedGroups: z6.array(z6.string()).optional()
|
|
4447
|
+
});
|
|
4448
|
+
pathPermissionSchema2 = z6.object({
|
|
4449
|
+
path: z6.string().min(1),
|
|
4450
|
+
read: permissionTargetSchema2.optional(),
|
|
4451
|
+
edit: permissionTargetSchema2.optional(),
|
|
4452
|
+
review: permissionTargetSchema2.optional()
|
|
4453
|
+
});
|
|
4454
|
+
}
|
|
4455
|
+
});
|
|
4456
|
+
init_helpers2 = __esm3({
|
|
4457
|
+
"dist/config/helpers.js"() {
|
|
4458
|
+
"use strict";
|
|
4459
|
+
init_validation2();
|
|
4460
|
+
}
|
|
4461
|
+
});
|
|
4462
|
+
init_config22 = __esm3({
|
|
4463
|
+
"dist/config/index.js"() {
|
|
4464
|
+
"use strict";
|
|
4465
|
+
init_types22();
|
|
4466
|
+
init_config3();
|
|
4467
|
+
init_field2();
|
|
4468
|
+
init_collection2();
|
|
4469
|
+
init_permissions2();
|
|
4125
4470
|
init_media2();
|
|
4126
4471
|
init_flatten2();
|
|
4127
4472
|
init_validation2();
|
|
@@ -4132,6 +4477,7 @@ var init_generate_ai_content = __esm({
|
|
|
4132
4477
|
"dist/operating-mode/client-unsafe-strategy.js"() {
|
|
4133
4478
|
"use strict";
|
|
4134
4479
|
init_client_safe_strategy();
|
|
4480
|
+
init_deployment_name();
|
|
4135
4481
|
init_config22();
|
|
4136
4482
|
ProdStrategy2 = class extends ProdClientSafeStrategy2 {
|
|
4137
4483
|
// All client-safe methods inherited automatically from ProdClientSafeStrategy:
|
|
@@ -4142,8 +4488,8 @@ var init_generate_ai_content = __esm({
|
|
|
4142
4488
|
getWorkspaceRoot(_sourceRoot) {
|
|
4143
4489
|
return path62.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE2);
|
|
4144
4490
|
}
|
|
4145
|
-
getContentRoot(sourceRoot) {
|
|
4146
|
-
return path62.resolve(sourceRoot ?? process.cwd(),
|
|
4491
|
+
getContentRoot(contentRoot, sourceRoot) {
|
|
4492
|
+
return path62.resolve(sourceRoot ?? process.cwd(), contentRoot);
|
|
4147
4493
|
}
|
|
4148
4494
|
getContentBranchesRoot(sourceRoot) {
|
|
4149
4495
|
return path62.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
|
|
@@ -4174,8 +4520,7 @@ var init_generate_ai_content = __esm({
|
|
|
4174
4520
|
getSettingsBranchName(config) {
|
|
4175
4521
|
if (config.settingsBranch)
|
|
4176
4522
|
return config.settingsBranch;
|
|
4177
|
-
|
|
4178
|
-
return `canopycms-settings-${deploymentName}`;
|
|
4523
|
+
return `canopycms-settings-${resolveDeploymentName2(config, "prod")}`;
|
|
4179
4524
|
}
|
|
4180
4525
|
getSettingsRoot(sourceRoot) {
|
|
4181
4526
|
return path62.join(this.getWorkspaceRoot(sourceRoot), "settings");
|
|
@@ -4197,8 +4542,8 @@ var init_generate_ai_content = __esm({
|
|
|
4197
4542
|
getWorkspaceRoot(sourceRoot) {
|
|
4198
4543
|
return path62.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
|
|
4199
4544
|
}
|
|
4200
|
-
getContentRoot(sourceRoot) {
|
|
4201
|
-
return path62.resolve(sourceRoot ?? process.cwd(),
|
|
4545
|
+
getContentRoot(contentRoot, sourceRoot) {
|
|
4546
|
+
return path62.resolve(sourceRoot ?? process.cwd(), contentRoot);
|
|
4202
4547
|
}
|
|
4203
4548
|
getContentBranchesRoot(sourceRoot) {
|
|
4204
4549
|
return path62.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
|
|
@@ -4228,8 +4573,7 @@ var init_generate_ai_content = __esm({
|
|
|
4228
4573
|
getSettingsBranchName(config) {
|
|
4229
4574
|
if (config.settingsBranch)
|
|
4230
4575
|
return config.settingsBranch;
|
|
4231
|
-
|
|
4232
|
-
return `canopycms-settings-${deploymentName}`;
|
|
4576
|
+
return `canopycms-settings-${resolveDeploymentName2(config, "local")}`;
|
|
4233
4577
|
}
|
|
4234
4578
|
getSettingsRoot(sourceRoot) {
|
|
4235
4579
|
return path62.join(this.getWorkspaceRoot(sourceRoot), "settings");
|
|
@@ -4251,13 +4595,15 @@ var init_generate_ai_content = __esm({
|
|
|
4251
4595
|
clearClientStrategyCache: () => clearClientStrategyCache,
|
|
4252
4596
|
clearStrategyCache: () => clearStrategyCache,
|
|
4253
4597
|
clientOperatingStrategy: () => clientOperatingStrategy,
|
|
4254
|
-
operatingStrategy: () => operatingStrategy2
|
|
4598
|
+
operatingStrategy: () => operatingStrategy2,
|
|
4599
|
+
resolveDeploymentName: () => resolveDeploymentName2
|
|
4255
4600
|
});
|
|
4256
4601
|
init_operating_mode = __esm3({
|
|
4257
4602
|
"dist/operating-mode/index.js"() {
|
|
4258
4603
|
"use strict";
|
|
4259
4604
|
init_client_safe_strategy();
|
|
4260
4605
|
init_client_unsafe_strategy();
|
|
4606
|
+
init_deployment_name();
|
|
4261
4607
|
}
|
|
4262
4608
|
});
|
|
4263
4609
|
locks = /* @__PURE__ */ new Map();
|
|
@@ -4284,7 +4630,7 @@ var init_generate_ai_content = __esm({
|
|
|
4284
4630
|
async scanDirectory(relativePath) {
|
|
4285
4631
|
const absoluteDir = path22.join(this.root, relativePath);
|
|
4286
4632
|
try {
|
|
4287
|
-
const entries = await
|
|
4633
|
+
const entries = await fs23.readdir(absoluteDir, { withFileTypes: true });
|
|
4288
4634
|
for (const entry of entries) {
|
|
4289
4635
|
if (entry.name.startsWith(".") || entry.name === "_ids_") {
|
|
4290
4636
|
continue;
|
|
@@ -4529,10 +4875,21 @@ var init_generate_ai_content = __esm({
|
|
|
4529
4875
|
const minLevel = this.options.minLevel ?? "DEBUG";
|
|
4530
4876
|
return LOG_LEVELS2[level] >= LOG_LEVELS2[minLevel];
|
|
4531
4877
|
}
|
|
4878
|
+
/**
|
|
4879
|
+
* The ISO-8601 timestamp leads the line BARE - not wrapped in brackets as it
|
|
4880
|
+
* once was. `CANOPYCMS_DEBUG=true` can be set on the EC2 worker, whose stdout
|
|
4881
|
+
* appends to /var/log/canopy-worker/worker.log; the CloudWatch agent's
|
|
4882
|
+
* `multi_line_start_pattern` matches the timestamp at the START of a line
|
|
4883
|
+
* (see worker/log.ts's INVARIANT and cms-service.ts's agent config), so a
|
|
4884
|
+
* leading `[` meant every debug line was silently folded into the previous
|
|
4885
|
+
* event instead of starting its own. The bracketed `[prefix:category]` and
|
|
4886
|
+
* `[level]` fields still follow, so the human-readable shape is unchanged
|
|
4887
|
+
* apart from those two characters.
|
|
4888
|
+
*/
|
|
4532
4889
|
formatMessage(level, category, message) {
|
|
4533
4890
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
4534
4891
|
const prefix = this.options.prefix ?? "CanopyCMS";
|
|
4535
|
-
return
|
|
4892
|
+
return `${timestamp} [${prefix}:${category}] [${level}] ${message}`;
|
|
4536
4893
|
}
|
|
4537
4894
|
debug(category, message, data) {
|
|
4538
4895
|
if (this.shouldLog("DEBUG")) {
|
|
@@ -4599,6 +4956,7 @@ var init_generate_ai_content = __esm({
|
|
|
4599
4956
|
});
|
|
4600
4957
|
log2 = createDebugLogger2({ prefix: "ResourceGeneration" });
|
|
4601
4958
|
META_DIR = ".canopy-meta";
|
|
4959
|
+
init_logger();
|
|
4602
4960
|
entryTypeMetaSchema = z7.object({
|
|
4603
4961
|
name: z7.string().min(1),
|
|
4604
4962
|
format: z7.enum(["md", "mdx", "json", "yaml"]),
|
|
@@ -4626,6 +4984,72 @@ var init_generate_ai_content = __esm({
|
|
|
4626
4984
|
// Embedded IDs for ordering items
|
|
4627
4985
|
});
|
|
4628
4986
|
init_flatten2();
|
|
4987
|
+
init_validation2();
|
|
4988
|
+
editDistance = (a, b) => {
|
|
4989
|
+
let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
4990
|
+
for (let i = 1; i <= a.length; i++) {
|
|
4991
|
+
const curr = [i];
|
|
4992
|
+
for (let j = 1; j <= b.length; j++) {
|
|
4993
|
+
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
4994
|
+
}
|
|
4995
|
+
prev = curr;
|
|
4996
|
+
}
|
|
4997
|
+
return prev[b.length];
|
|
4998
|
+
};
|
|
4999
|
+
closestName = (name, known) => {
|
|
5000
|
+
let best;
|
|
5001
|
+
let bestDistance = Infinity;
|
|
5002
|
+
for (const candidate of known) {
|
|
5003
|
+
const distance = editDistance(name.toLowerCase(), candidate.toLowerCase());
|
|
5004
|
+
if (distance < bestDistance) {
|
|
5005
|
+
bestDistance = distance;
|
|
5006
|
+
best = candidate;
|
|
5007
|
+
}
|
|
5008
|
+
}
|
|
5009
|
+
const threshold = Math.max(2, Math.floor(name.length / 3));
|
|
5010
|
+
return best !== void 0 && bestDistance <= threshold ? best : void 0;
|
|
5011
|
+
};
|
|
5012
|
+
forEachEntryType = (schema, visit) => {
|
|
5013
|
+
const walk = (node, nodePath) => {
|
|
5014
|
+
for (const entryType of node.entries ?? []) {
|
|
5015
|
+
visit(entryType, nodePath);
|
|
5016
|
+
}
|
|
5017
|
+
for (const child of node.collections ?? []) {
|
|
5018
|
+
walk(child, child.path ?? (nodePath ? `${nodePath}/${child.name}` : child.name));
|
|
5019
|
+
}
|
|
5020
|
+
};
|
|
5021
|
+
walk(schema, "");
|
|
5022
|
+
};
|
|
5023
|
+
collectEntryTypeNames = (schema) => {
|
|
5024
|
+
const names = /* @__PURE__ */ new Set();
|
|
5025
|
+
forEachEntryType(schema, (entryType) => {
|
|
5026
|
+
if (typeof entryType.name === "string" && entryType.name.length > 0) {
|
|
5027
|
+
names.add(entryType.name);
|
|
5028
|
+
}
|
|
5029
|
+
});
|
|
5030
|
+
return names;
|
|
5031
|
+
};
|
|
5032
|
+
validateReferenceEntryTypes = (schema) => {
|
|
5033
|
+
const known = collectEntryTypeNames(schema);
|
|
5034
|
+
const knownList = [...known].sort();
|
|
5035
|
+
const issues = [];
|
|
5036
|
+
forEachEntryType(schema, (entryType, collectionPath) => {
|
|
5037
|
+
const location = collectionPath ? `${collectionPath}/${entryType.name}` : entryType.name;
|
|
5038
|
+
forEachReferenceField(entryType.schema, (field) => {
|
|
5039
|
+
const entryTypes = field.entryTypes;
|
|
5040
|
+
if (!Array.isArray(entryTypes))
|
|
5041
|
+
return;
|
|
5042
|
+
const fieldName = typeof field.name === "string" ? field.name : "unknown";
|
|
5043
|
+
for (const value of entryTypes) {
|
|
5044
|
+
if (typeof value !== "string" || known.has(value))
|
|
5045
|
+
continue;
|
|
5046
|
+
const suggestion = closestName(value, knownList);
|
|
5047
|
+
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."));
|
|
5048
|
+
}
|
|
5049
|
+
});
|
|
5050
|
+
});
|
|
5051
|
+
return issues;
|
|
5052
|
+
};
|
|
4629
5053
|
isDeployedStatic = (config) => {
|
|
4630
5054
|
return config.deployedAs === "static";
|
|
4631
5055
|
};
|
|
@@ -4737,6 +5161,11 @@ var init_generate_ai_content = __esm({
|
|
|
4737
5161
|
if (!isValidSchema(result.schema)) {
|
|
4738
5162
|
throw new Error(`No schema found in ${contentRoot}. Create .collection.json files with references to field schemas defined in your entry schema registry.`);
|
|
4739
5163
|
}
|
|
5164
|
+
const entryTypeIssues = validateReferenceEntryTypes(result.schema);
|
|
5165
|
+
if (entryTypeIssues.length > 0) {
|
|
5166
|
+
throw new Error(`Invalid reference field entryTypes in ${contentRoot}:
|
|
5167
|
+
` + entryTypeIssues.map((issue) => ` - ${issue}`).join("\n"));
|
|
5168
|
+
}
|
|
4740
5169
|
const flatSchema = flattenSchema(result.schema, contentRootName);
|
|
4741
5170
|
if (!skipDiskCache) {
|
|
4742
5171
|
const cacheDir = path52.join(branchRoot, ".canopy-meta");
|
|
@@ -4854,7 +5283,8 @@ var init_generate_ai_content = __esm({
|
|
|
4854
5283
|
this.lastDiskProbeMs = 0;
|
|
4855
5284
|
this.lastForcedRefreshMs = 0;
|
|
4856
5285
|
this.indexBuild = null;
|
|
4857
|
-
this.root =
|
|
5286
|
+
this.root = path82.resolve(root);
|
|
5287
|
+
this.contentRootName = options.contentRootName || "content";
|
|
4858
5288
|
this.indexFreshnessIntervalMs = options.indexFreshnessIntervalMs ?? DEFAULT_INDEX_FRESHNESS_INTERVAL_MS;
|
|
4859
5289
|
this.schemaIndex = new Map(flatSchema.map((item) => [item.logicalPath, item]));
|
|
4860
5290
|
this._idIndex = new ContentIdIndex(this.root);
|
|
@@ -4896,7 +5326,7 @@ var init_generate_ai_content = __esm({
|
|
|
4896
5326
|
const build = (async () => {
|
|
4897
5327
|
const diskToken = await readContentIndexGeneration(this.root);
|
|
4898
5328
|
const fresh = new ContentIdIndex(this.root);
|
|
4899
|
-
await fresh.buildFromFilenames(
|
|
5329
|
+
await fresh.buildFromFilenames(this.contentRootName);
|
|
4900
5330
|
return { diskToken, fresh };
|
|
4901
5331
|
})();
|
|
4902
5332
|
this.indexBuild = build;
|
|
@@ -4979,7 +5409,7 @@ var init_generate_ai_content = __esm({
|
|
|
4979
5409
|
if (entry.isDirectory())
|
|
4980
5410
|
continue;
|
|
4981
5411
|
if (extractIdFromFilename(entry.name) === id) {
|
|
4982
|
-
return
|
|
5412
|
+
return path82.relative(this.root, path82.join(dir, entry.name));
|
|
4983
5413
|
}
|
|
4984
5414
|
}
|
|
4985
5415
|
return null;
|
|
@@ -5078,7 +5508,7 @@ var init_generate_ai_content = __esm({
|
|
|
5078
5508
|
* @param options.entryTypeName - For collections with multiple entry types, specify which one to use. Defaults to the default entry type.
|
|
5079
5509
|
*/
|
|
5080
5510
|
async buildPaths(schemaItem, slug, options = {}) {
|
|
5081
|
-
const rootWithSep = this.root.endsWith(
|
|
5511
|
+
const rootWithSep = this.root.endsWith(path82.sep) ? this.root : `${this.root}${path82.sep}`;
|
|
5082
5512
|
if (schemaItem.type === "entry-type") {
|
|
5083
5513
|
const parentPath = schemaItem.parentPath || "";
|
|
5084
5514
|
const parentCollection = this.schemaIndex.get(parentPath);
|
|
@@ -5111,7 +5541,7 @@ var init_generate_ai_content = __esm({
|
|
|
5111
5541
|
const entryTypeName = entryTypeConfig?.name || "entry";
|
|
5112
5542
|
let collectionRoot = await resolveCollectionPath(this.root, schemaItem.logicalPath);
|
|
5113
5543
|
if (!collectionRoot) {
|
|
5114
|
-
collectionRoot =
|
|
5544
|
+
collectionRoot = path82.resolve(this.root, schemaItem.logicalPath);
|
|
5115
5545
|
}
|
|
5116
5546
|
if (!collectionRoot.startsWith(rootWithSep)) {
|
|
5117
5547
|
throw new ContentStoreError("Path traversal detected", "VALIDATION");
|
|
@@ -5147,14 +5577,14 @@ var init_generate_ai_content = __esm({
|
|
|
5147
5577
|
}
|
|
5148
5578
|
filename = `${finalEntryTypeName}.${safeSlug}.${id}${ext}`;
|
|
5149
5579
|
}
|
|
5150
|
-
const resolved =
|
|
5151
|
-
const collectionRootWithSep = collectionRoot.endsWith(
|
|
5580
|
+
const resolved = path82.resolve(collectionRoot, filename);
|
|
5581
|
+
const collectionRootWithSep = collectionRoot.endsWith(path82.sep) ? collectionRoot : `${collectionRoot}${path82.sep}`;
|
|
5152
5582
|
if (!resolved.startsWith(collectionRootWithSep)) {
|
|
5153
5583
|
throw new ContentStoreError("Path traversal detected", "VALIDATION");
|
|
5154
5584
|
}
|
|
5155
5585
|
return {
|
|
5156
5586
|
absolutePath: resolved,
|
|
5157
|
-
relativePath:
|
|
5587
|
+
relativePath: path82.relative(this.root, resolved),
|
|
5158
5588
|
id,
|
|
5159
5589
|
entryTypeName: finalEntryTypeName,
|
|
5160
5590
|
existed
|
|
@@ -5287,7 +5717,7 @@ var init_generate_ai_content = __esm({
|
|
|
5287
5717
|
return RETRY_KEY;
|
|
5288
5718
|
}
|
|
5289
5719
|
const { absolutePath, relativePath, id } = inLock;
|
|
5290
|
-
await fs8.mkdir(
|
|
5720
|
+
await fs8.mkdir(path82.dirname(absolutePath), { recursive: true });
|
|
5291
5721
|
if (input.expectedVersion !== void 0) {
|
|
5292
5722
|
try {
|
|
5293
5723
|
const existing = await fs8.stat(absolutePath);
|
|
@@ -5304,7 +5734,7 @@ var init_generate_ai_content = __esm({
|
|
|
5304
5734
|
}
|
|
5305
5735
|
}
|
|
5306
5736
|
if (existingId && !await filePathExists3(absolutePath)) {
|
|
5307
|
-
const actualRelPath = await this.findEntryPathById(
|
|
5737
|
+
const actualRelPath = await this.findEntryPathById(path82.dirname(absolutePath), existingId);
|
|
5308
5738
|
const indexedRelPath = this._idIndex.findById(existingId)?.relativePath ?? null;
|
|
5309
5739
|
if (actualRelPath !== null && actualRelPath !== indexedRelPath) {
|
|
5310
5740
|
throw new ContentConflictError();
|
|
@@ -5326,7 +5756,7 @@ var init_generate_ai_content = __esm({
|
|
|
5326
5756
|
const existing = liveIndex.findById(id);
|
|
5327
5757
|
if (existing) {
|
|
5328
5758
|
if (existing.relativePath !== relativePath) {
|
|
5329
|
-
staleOldAbsPath =
|
|
5759
|
+
staleOldAbsPath = path82.join(this.root, existing.relativePath);
|
|
5330
5760
|
liveIndex.updatePath(existing.id, relativePath);
|
|
5331
5761
|
}
|
|
5332
5762
|
} else {
|
|
@@ -5560,7 +5990,7 @@ var init_generate_ai_content = __esm({
|
|
|
5560
5990
|
}
|
|
5561
5991
|
destLockKey = currentDestKey;
|
|
5562
5992
|
const { absolutePath: currentPath, relativePath: currentRelPath } = inLock;
|
|
5563
|
-
const currentFilename =
|
|
5993
|
+
const currentFilename = path82.basename(currentPath);
|
|
5564
5994
|
const parts = currentFilename.split(".");
|
|
5565
5995
|
if (parts.length < 4) {
|
|
5566
5996
|
throw new ContentStoreError(`Invalid entry filename format: ${currentFilename}`, "VALIDATION");
|
|
@@ -5569,8 +5999,8 @@ var init_generate_ai_content = __esm({
|
|
|
5569
5999
|
const contentId = parts[parts.length - 2];
|
|
5570
6000
|
const ext = `.${parts[parts.length - 1]}`;
|
|
5571
6001
|
const newFilename = `${entryTypeName}.${safeNewSlug}.${contentId}${ext}`;
|
|
5572
|
-
const parentDir =
|
|
5573
|
-
const newPath =
|
|
6002
|
+
const parentDir = path82.dirname(currentPath);
|
|
6003
|
+
const newPath = path82.join(parentDir, newFilename);
|
|
5574
6004
|
try {
|
|
5575
6005
|
const entries = await fs8.readdir(parentDir, { withFileTypes: true });
|
|
5576
6006
|
for (const entry of entries) {
|
|
@@ -5594,7 +6024,7 @@ var init_generate_ai_content = __esm({
|
|
|
5594
6024
|
throw err;
|
|
5595
6025
|
}
|
|
5596
6026
|
await fs8.unlink(currentPath);
|
|
5597
|
-
const newRelativePath =
|
|
6027
|
+
const newRelativePath = path82.relative(this.root, newPath);
|
|
5598
6028
|
const liveIndex = this._idIndex;
|
|
5599
6029
|
const entryId = liveIndex.findByPath(currentRelPath);
|
|
5600
6030
|
if (entryId) {
|
|
@@ -5781,6 +6211,7 @@ var init_generate_ai_content = __esm({
|
|
|
5781
6211
|
log22 = createDebugLogger2({ prefix: "EntryLinks" });
|
|
5782
6212
|
BASE58_CHAR = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]";
|
|
5783
6213
|
ENTRY_LINK_PATTERN = new RegExp(`entry:(${BASE58_CHAR}{12})(#[^\\s)>"']*)?`, "g");
|
|
6214
|
+
init_logger();
|
|
5784
6215
|
log3 = createDebugLogger2({ prefix: "BranchRegistry" });
|
|
5785
6216
|
REGISTRY_FILE = "branches.json";
|
|
5786
6217
|
REGISTRY_STALE_FILE = "branches.stale.json";
|
|
@@ -5807,7 +6238,7 @@ var init_generate_ai_content = __esm({
|
|
|
5807
6238
|
const raw = await fs10.readFile(this.registryPath, "utf8");
|
|
5808
6239
|
parsed = JSON.parse(raw);
|
|
5809
6240
|
} catch (err) {
|
|
5810
|
-
if (
|
|
6241
|
+
if (isNotFoundError3(err)) {
|
|
5811
6242
|
return await this.regenerate();
|
|
5812
6243
|
}
|
|
5813
6244
|
throw err;
|
|
@@ -5933,7 +6364,7 @@ var init_generate_ai_content = __esm({
|
|
|
5933
6364
|
try {
|
|
5934
6365
|
meta = await BranchMetadataFileManager.loadOnly(branchRoot);
|
|
5935
6366
|
} catch (err) {
|
|
5936
|
-
|
|
6367
|
+
canopyLogWarn2(`CanopyCMS: Skipping branch directory '${entry.name}' during registry scan: ${getErrorMessage3(err)}`);
|
|
5937
6368
|
continue;
|
|
5938
6369
|
}
|
|
5939
6370
|
if (meta) {
|
|
@@ -5945,7 +6376,7 @@ var init_generate_ai_content = __esm({
|
|
|
5945
6376
|
}
|
|
5946
6377
|
}
|
|
5947
6378
|
} catch (err) {
|
|
5948
|
-
if (
|
|
6379
|
+
if (isNotFoundError3(err)) {
|
|
5949
6380
|
return [];
|
|
5950
6381
|
}
|
|
5951
6382
|
throw err;
|
|
@@ -5995,7 +6426,7 @@ var init_generate_ai_content = __esm({
|
|
|
5995
6426
|
const raw = await fs12.readFile(filePath, "utf8");
|
|
5996
6427
|
return JSON.parse(raw);
|
|
5997
6428
|
} catch (err) {
|
|
5998
|
-
if (
|
|
6429
|
+
if (isNotFoundError3(err)) {
|
|
5999
6430
|
return null;
|
|
6000
6431
|
}
|
|
6001
6432
|
if (err instanceof SyntaxError) {
|
|
@@ -6018,7 +6449,7 @@ var init_generate_ai_content = __esm({
|
|
|
6018
6449
|
const version = parsed.version ?? 0;
|
|
6019
6450
|
return { meta: parsed, version };
|
|
6020
6451
|
} catch (err) {
|
|
6021
|
-
if (
|
|
6452
|
+
if (isNotFoundError3(err)) {
|
|
6022
6453
|
return { meta: null, version: null };
|
|
6023
6454
|
}
|
|
6024
6455
|
throw err;
|
|
@@ -6077,7 +6508,7 @@ var init_generate_ai_content = __esm({
|
|
|
6077
6508
|
try {
|
|
6078
6509
|
await fs12.stat(this.branchRoot);
|
|
6079
6510
|
} catch (err) {
|
|
6080
|
-
if (
|
|
6511
|
+
if (isNotFoundError3(err)) {
|
|
6081
6512
|
throw new BranchMetadataConflictError("Branch no longer exists");
|
|
6082
6513
|
}
|
|
6083
6514
|
throw err;
|
|
@@ -6166,7 +6597,9 @@ var init_generate_ai_content = __esm({
|
|
|
6166
6597
|
COLON_BEFORE_SLASH_PATTERN = /^[^/\\\s]+:/;
|
|
6167
6598
|
log5 = createDebugLogger2({ prefix: "GitManager" });
|
|
6168
6599
|
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_]*)$/;
|
|
6600
|
+
FORCE_C_LOCALE = { LC_ALL: "C", LANG: "C" };
|
|
6169
6601
|
remoteInitLocks = /* @__PURE__ */ new Map();
|
|
6602
|
+
GITHUB_TRACKING_REF_PREFIX = "refs/remotes/github/";
|
|
6170
6603
|
GitConflictError = class extends Error {
|
|
6171
6604
|
constructor(conflictedFiles) {
|
|
6172
6605
|
super(`Git conflict in ${conflictedFiles.length} file(s): ${conflictedFiles.join(", ")}`);
|
|
@@ -6230,7 +6663,7 @@ var init_generate_ai_content = __esm({
|
|
|
6230
6663
|
const stat = await fs14.stat(options.remotePath);
|
|
6231
6664
|
remoteExists = stat.isDirectory();
|
|
6232
6665
|
} catch (err) {
|
|
6233
|
-
if (!
|
|
6666
|
+
if (!isNotFoundError3(err))
|
|
6234
6667
|
throw err;
|
|
6235
6668
|
}
|
|
6236
6669
|
if (remoteExists && await _GitManager.bareRemoteHasBranch(options.remotePath, options.baseBranch)) {
|
|
@@ -6301,26 +6734,115 @@ var init_generate_ai_content = __esm({
|
|
|
6301
6734
|
await lockPromise;
|
|
6302
6735
|
}
|
|
6303
6736
|
/**
|
|
6304
|
-
* Whether a bare repository already has a
|
|
6737
|
+
* Whether a bare repository already has a branch of the given name, in
|
|
6738
|
+
* EITHER the local-heads namespace (`refs/heads/<branch>`) or the GitHub
|
|
6739
|
+
* tracking namespace (`GITHUB_TRACKING_REF_PREFIX<branch>`).
|
|
6740
|
+
*
|
|
6741
|
+
* Originally checked `refs/heads/*` only, for dev-mode's
|
|
6742
|
+
* `ensureLocalSimulatedRemote` (a simulated remote never gets a tracking
|
|
6743
|
+
* namespace, so that caller only ever needed the local-heads check).
|
|
6744
|
+
* Generalized/promoted to public for api/branch.ts's create-time collision
|
|
6745
|
+
* guard, which also needs the tracking namespace: `syncGit()` fetches
|
|
6746
|
+
* GitHub into `GITHUB_TRACKING_REF_PREFIX*` rather than `refs/heads/*`
|
|
6747
|
+
* directly (see that constant's doc comment above), and
|
|
6748
|
+
* `reconcileTrackedBranches()` (worker/cms-worker.ts) only
|
|
6749
|
+
* non-destructively brings `refs/heads/*` toward what's tracked there — so
|
|
6750
|
+
* a branch that another CanopyCMS deployment sharing this repo (or a
|
|
6751
|
+
* direct push to GitHub) just created can sit in the tracking namespace
|
|
6752
|
+
* for a while before, or without ever, gaining a local head here. Checking
|
|
6753
|
+
* `refs/heads/*` alone would miss exactly the two-deployments-one-repo
|
|
6754
|
+
* collision that guard exists to catch.
|
|
6305
6755
|
*
|
|
6306
6756
|
* Runs git with an explicit `--git-dir` instead of a cwd inside the repo:
|
|
6307
6757
|
* environments with `safe.bareRepository=explicit` (sandboxed/CI git setups)
|
|
6308
6758
|
* refuse cwd-based discovery of bare repos but expressly allow `--git-dir`.
|
|
6309
|
-
*
|
|
6310
|
-
* exception-based control flow —
|
|
6311
|
-
*
|
|
6312
|
-
*
|
|
6313
|
-
*
|
|
6314
|
-
*
|
|
6759
|
+
* A single `for-each-ref` call checks both candidate refs at once (no
|
|
6760
|
+
* exception-based control flow — `for-each-ref` exits 0 whether or not
|
|
6761
|
+
* either ref exists, same reason the old implementation used `branch
|
|
6762
|
+
* --list` instead of `rev-parse --verify --quiet`: simple-git only fails a
|
|
6763
|
+
* task on stderr output, and `--quiet` suppresses exactly that).
|
|
6764
|
+
* `--end-of-options` guards the ref-name positionals the same way
|
|
6765
|
+
* `push`/`forcePush` below do, since `branch` here can be a sanitized but
|
|
6766
|
+
* otherwise caller-influenced string.
|
|
6767
|
+
*
|
|
6768
|
+
* A failure here therefore means the remote itself is unreadable and is
|
|
6769
|
+
* surfaced, NOT treated as "branch absent" — that would route
|
|
6770
|
+
* `ensureLocalSimulatedRemote` to the push path against a repo it couldn't
|
|
6771
|
+
* even read, and would silently skip api/branch.ts's collision check
|
|
6772
|
+
* instead of letting that caller distinguish "unreadable" from "absent"
|
|
6773
|
+
* and decide what to do.
|
|
6315
6774
|
*/
|
|
6316
|
-
static async bareRemoteHasBranch(remotePath, branch) {
|
|
6775
|
+
static async bareRemoteHasBranch(remotePath, branch, options = {}) {
|
|
6317
6776
|
let output;
|
|
6318
6777
|
try {
|
|
6319
|
-
output = await simpleGit2().raw([
|
|
6778
|
+
output = await simpleGit2().raw([
|
|
6779
|
+
"--git-dir",
|
|
6780
|
+
remotePath,
|
|
6781
|
+
"for-each-ref",
|
|
6782
|
+
"--format=%(refname)",
|
|
6783
|
+
"--end-of-options",
|
|
6784
|
+
`refs/heads/${branch}`,
|
|
6785
|
+
`${GITHUB_TRACKING_REF_PREFIX}${branch}`
|
|
6786
|
+
]);
|
|
6320
6787
|
} catch (err) {
|
|
6321
|
-
throw new Error(`Cannot inspect
|
|
6788
|
+
throw new Error(`Cannot inspect remote mirror at ${remotePath}: ${getErrorMessage3(err)}`);
|
|
6789
|
+
}
|
|
6790
|
+
const refs = output.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
6791
|
+
if (options.namespaces === "tracking") {
|
|
6792
|
+
return refs.includes(`${GITHUB_TRACKING_REF_PREFIX}${branch}`);
|
|
6322
6793
|
}
|
|
6323
|
-
return
|
|
6794
|
+
return refs.includes(`refs/heads/${branch}`) || refs.includes(`${GITHUB_TRACKING_REF_PREFIX}${branch}`);
|
|
6795
|
+
}
|
|
6796
|
+
/**
|
|
6797
|
+
* Delete `refs/heads/<branch>` from a bare local mirror, if present. A
|
|
6798
|
+
* no-op (not an error) when the ref doesn't exist.
|
|
6799
|
+
*
|
|
6800
|
+
* This is the "explicit path" for removing a deleted branch's local head
|
|
6801
|
+
* that the sync loop deliberately is not (see GITHUB_TRACKING_REF_PREFIX's
|
|
6802
|
+
* doc comment: reconcileTrackedBranches never deletes a head). Called by
|
|
6803
|
+
* api/branch.ts's deleteBranchHandler: without it, a deleted branch's head
|
|
6804
|
+
* lives in `remote.git` forever, and the ordinary create -> publish ->
|
|
6805
|
+
* squash-merge -> delete -> reuse-the-name cycle then has the REUSED
|
|
6806
|
+
* branch's first publish rejected non-fast-forward against the stale head
|
|
6807
|
+
* (`GitManager.push()` pushes `branch:branch`, and a squash-merged old tip
|
|
6808
|
+
* is not an ancestor of the new branch) -- a permanent, misleading 409.
|
|
6809
|
+
* Worse, a retried submit skips the local push (clean tree) and enqueues
|
|
6810
|
+
* the worker push of the STALE head, resurrecting the deleted branch's
|
|
6811
|
+
* content on GitHub as an apparent success.
|
|
6812
|
+
*
|
|
6813
|
+
* Deliberately leaves the tracking ref (`GITHUB_TRACKING_REF_PREFIX<branch>`)
|
|
6814
|
+
* alone: that namespace mirrors GitHub's view, and if the branch still
|
|
6815
|
+
* exists on GitHub, the create-time collision check SHOULD keep reporting
|
|
6816
|
+
* it until the remote side is actually gone.
|
|
6817
|
+
*
|
|
6818
|
+
* Same `--git-dir` invocation style as bareRemoteHasBranch above (works
|
|
6819
|
+
* under `safe.bareRepository=explicit`). The existence pre-check makes
|
|
6820
|
+
* "absent" deterministic instead of parsing update-ref's locale-dependent
|
|
6821
|
+
* failure text, and its captured SHA is passed to `update-ref -d` as the
|
|
6822
|
+
* expected old value -- same pattern as reconcileTrackedBranches'
|
|
6823
|
+
* guarded updates (worker/cms-worker.ts): a concurrent Lambda push
|
|
6824
|
+
* re-creating/moving this branch between the read and the delete makes
|
|
6825
|
+
* update-ref throw (surfaced as the caller's best-effort warning) instead
|
|
6826
|
+
* of silently deleting a commit that was just pushed. No
|
|
6827
|
+
* `--end-of-options` on update-ref (older gits don't accept it there);
|
|
6828
|
+
* the ref argument always begins with the literal `refs/heads/` prefix,
|
|
6829
|
+
* so it can never parse as an option.
|
|
6830
|
+
*/
|
|
6831
|
+
static async deleteBareRemoteHead(remotePath, branch) {
|
|
6832
|
+
const ref = `refs/heads/${branch}`;
|
|
6833
|
+
const output = await simpleGit2().raw([
|
|
6834
|
+
"--git-dir",
|
|
6835
|
+
remotePath,
|
|
6836
|
+
"for-each-ref",
|
|
6837
|
+
"--format=%(refname) %(objectname)",
|
|
6838
|
+
"--end-of-options",
|
|
6839
|
+
ref
|
|
6840
|
+
]);
|
|
6841
|
+
const match = output.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => line.split(" ")).find(([refname]) => refname === ref);
|
|
6842
|
+
if (!match)
|
|
6843
|
+
return;
|
|
6844
|
+
const [, sha] = match;
|
|
6845
|
+
await simpleGit2().raw(["--git-dir", remotePath, "update-ref", "-d", ref, sha]);
|
|
6324
6846
|
}
|
|
6325
6847
|
/**
|
|
6326
6848
|
* Push baseBranch from the source repo into the local bare remote via a
|
|
@@ -6395,7 +6917,7 @@ var init_generate_ai_content = __esm({
|
|
|
6395
6917
|
throw new Error(`Expected git repo at ${repoPath}`);
|
|
6396
6918
|
}
|
|
6397
6919
|
} catch (err) {
|
|
6398
|
-
if (
|
|
6920
|
+
if (isNotFoundError3(err)) {
|
|
6399
6921
|
throw new Error(`Expected git repo at ${repoPath}`);
|
|
6400
6922
|
}
|
|
6401
6923
|
throw err;
|
|
@@ -6493,6 +7015,30 @@ var init_generate_ai_content = __esm({
|
|
|
6493
7015
|
}
|
|
6494
7016
|
return void 0;
|
|
6495
7017
|
}
|
|
7018
|
+
/**
|
|
7019
|
+
* Check whether a git repository is already initialized at `workspacePath`.
|
|
7020
|
+
*
|
|
7021
|
+
* Uses `rev-parse --git-dir` with `GIT_CEILING_DIRECTORIES` pinned to the
|
|
7022
|
+
* parent directory so a corrupt/missing `.git` can't make git silently
|
|
7023
|
+
* traverse upward and report a false positive from an ancestor repo (the
|
|
7024
|
+
* same protection `initializeWorkspace` relies on below).
|
|
7025
|
+
*
|
|
7026
|
+
* Shared by `initializeWorkspace` (clone-vs-reuse decision) and
|
|
7027
|
+
* `SettingsWorkspaceManager`'s rename guard (settings-workspace.ts), which
|
|
7028
|
+
* must know whether a settings workspace ALREADY exists before touching it —
|
|
7029
|
+
* touching an existing orphan settings branch under a different name wipes
|
|
7030
|
+
* permissions.json/groups.json (see that guard's doc comment).
|
|
7031
|
+
*/
|
|
7032
|
+
static async repoExistsAt(workspacePath) {
|
|
7033
|
+
try {
|
|
7034
|
+
const checkGit = simpleGit2({ baseDir: workspacePath });
|
|
7035
|
+
checkGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path14.dirname(workspacePath) }));
|
|
7036
|
+
await checkGit.raw(["rev-parse", "--git-dir"]);
|
|
7037
|
+
return true;
|
|
7038
|
+
} catch {
|
|
7039
|
+
return false;
|
|
7040
|
+
}
|
|
7041
|
+
}
|
|
6496
7042
|
/**
|
|
6497
7043
|
* Ensures a git workspace is initialized and ready for use.
|
|
6498
7044
|
* Handles cloning, remote configuration, and branch checkout/creation.
|
|
@@ -6511,13 +7057,8 @@ var init_generate_ai_content = __esm({
|
|
|
6511
7057
|
detectFrom: options.sourceRoot ? path14.resolve(process.cwd(), options.sourceRoot) : process.cwd()
|
|
6512
7058
|
});
|
|
6513
7059
|
const remoteName = options.remoteName ?? "origin";
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
const checkGit = simpleGit2({ baseDir: options.workspacePath });
|
|
6517
|
-
checkGit.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path14.dirname(options.workspacePath) }));
|
|
6518
|
-
await checkGit.raw(["rev-parse", "--git-dir"]);
|
|
6519
|
-
repoExists = true;
|
|
6520
|
-
} catch {
|
|
7060
|
+
const repoExists = await _GitManager.repoExistsAt(options.workspacePath);
|
|
7061
|
+
if (!repoExists) {
|
|
6521
7062
|
const gitPath = path14.join(options.workspacePath, ".git");
|
|
6522
7063
|
try {
|
|
6523
7064
|
const stat = await fs14.stat(gitPath);
|
|
@@ -6528,7 +7069,7 @@ var init_generate_ai_content = __esm({
|
|
|
6528
7069
|
await fs14.rm(gitPath, { recursive: true });
|
|
6529
7070
|
}
|
|
6530
7071
|
} catch (cleanupErr) {
|
|
6531
|
-
if (!
|
|
7072
|
+
if (!isNotFoundError3(cleanupErr))
|
|
6532
7073
|
throw cleanupErr;
|
|
6533
7074
|
}
|
|
6534
7075
|
}
|
|
@@ -6987,7 +7528,7 @@ var init_generate_ai_content = __esm({
|
|
|
6987
7528
|
}
|
|
6988
7529
|
return data;
|
|
6989
7530
|
} catch (err) {
|
|
6990
|
-
if (
|
|
7531
|
+
if (isNotFoundError3(err))
|
|
6991
7532
|
return {};
|
|
6992
7533
|
log7.warn("readEntryData", `Failed to read entry data from ${filePath}: ${getErrorMessage3(err)}`);
|
|
6993
7534
|
return {};
|
|
@@ -7038,7 +7579,7 @@ var init_generate_ai_content = __esm({
|
|
|
7038
7579
|
try {
|
|
7039
7580
|
dirents = await fs15.readdir(collectionRoot, { withFileTypes: true });
|
|
7040
7581
|
} catch (err) {
|
|
7041
|
-
if (
|
|
7582
|
+
if (isNotFoundError3(err))
|
|
7042
7583
|
return [];
|
|
7043
7584
|
throw err;
|
|
7044
7585
|
}
|
|
@@ -7132,10 +7673,21 @@ var init_debug2 = __esm({
|
|
|
7132
7673
|
const minLevel = this.options.minLevel ?? "DEBUG";
|
|
7133
7674
|
return LOG_LEVELS3[level] >= LOG_LEVELS3[minLevel];
|
|
7134
7675
|
}
|
|
7676
|
+
/**
|
|
7677
|
+
* The ISO-8601 timestamp leads the line BARE - not wrapped in brackets as it
|
|
7678
|
+
* once was. `CANOPYCMS_DEBUG=true` can be set on the EC2 worker, whose stdout
|
|
7679
|
+
* appends to /var/log/canopy-worker/worker.log; the CloudWatch agent's
|
|
7680
|
+
* `multi_line_start_pattern` matches the timestamp at the START of a line
|
|
7681
|
+
* (see worker/log.ts's INVARIANT and cms-service.ts's agent config), so a
|
|
7682
|
+
* leading `[` meant every debug line was silently folded into the previous
|
|
7683
|
+
* event instead of starting its own. The bracketed `[prefix:category]` and
|
|
7684
|
+
* `[level]` fields still follow, so the human-readable shape is unchanged
|
|
7685
|
+
* apart from those two characters.
|
|
7686
|
+
*/
|
|
7135
7687
|
formatMessage(level, category, message) {
|
|
7136
7688
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
7137
7689
|
const prefix = this.options.prefix ?? "CanopyCMS";
|
|
7138
|
-
return
|
|
7690
|
+
return `${timestamp} [${prefix}:${category}] [${level}] ${message}`;
|
|
7139
7691
|
}
|
|
7140
7692
|
debug(category, message, data) {
|
|
7141
7693
|
if (this.shouldLog("DEBUG")) {
|
|
@@ -7288,6 +7840,21 @@ var init_content_index_registry = __esm({
|
|
|
7288
7840
|
}
|
|
7289
7841
|
});
|
|
7290
7842
|
|
|
7843
|
+
// dist/utils/logger.js
|
|
7844
|
+
function canopyLogWarn3(...args) {
|
|
7845
|
+
active3.warn(...args);
|
|
7846
|
+
}
|
|
7847
|
+
function canopyLogError2(...args) {
|
|
7848
|
+
active3.error(...args);
|
|
7849
|
+
}
|
|
7850
|
+
var active3;
|
|
7851
|
+
var init_logger2 = __esm({
|
|
7852
|
+
"dist/utils/logger.js"() {
|
|
7853
|
+
"use strict";
|
|
7854
|
+
active3 = console;
|
|
7855
|
+
}
|
|
7856
|
+
});
|
|
7857
|
+
|
|
7291
7858
|
// dist/paths/normalize.js
|
|
7292
7859
|
var init_normalize3 = __esm({
|
|
7293
7860
|
"dist/paths/normalize.js"() {
|
|
@@ -7443,7 +8010,7 @@ async function scanForCollectionMeta2(baseDir, relativePath = "") {
|
|
|
7443
8010
|
collections.push(...nestedCollections);
|
|
7444
8011
|
} catch (err) {
|
|
7445
8012
|
if (err.code !== "ENOENT") {
|
|
7446
|
-
|
|
8013
|
+
canopyLogError2(`Error loading ${metaPath}:`, err);
|
|
7447
8014
|
throw new Error(`Invalid .collection.json in ${folderPath}: ${err.message}`);
|
|
7448
8015
|
}
|
|
7449
8016
|
const nestedCollections = await scanForCollectionMeta2(absolutePath, folderPath);
|
|
@@ -7487,6 +8054,7 @@ var init_meta_loader = __esm({
|
|
|
7487
8054
|
"dist/schema/meta-loader.js"() {
|
|
7488
8055
|
"use strict";
|
|
7489
8056
|
init_error();
|
|
8057
|
+
init_logger2();
|
|
7490
8058
|
init_content_id_index();
|
|
7491
8059
|
entryTypeMetaSchema2 = z8.object({
|
|
7492
8060
|
name: z8.string().min(1),
|
|
@@ -7542,87 +8110,386 @@ var init_flatten3 = __esm({
|
|
|
7542
8110
|
}
|
|
7543
8111
|
});
|
|
7544
8112
|
|
|
7545
|
-
// dist/
|
|
7546
|
-
var
|
|
7547
|
-
var
|
|
7548
|
-
"dist/
|
|
7549
|
-
"use strict";
|
|
7550
|
-
STATIC_DEPLOY_USER2 = Object.freeze({
|
|
7551
|
-
type: "authenticated",
|
|
7552
|
-
userId: "__static_deploy__",
|
|
7553
|
-
groups: ["Admins"],
|
|
7554
|
-
email: "static-deploy@canopycms",
|
|
7555
|
-
name: "Static Deploy"
|
|
7556
|
-
});
|
|
7557
|
-
}
|
|
7558
|
-
});
|
|
7559
|
-
|
|
7560
|
-
// dist/branch-schema-cache.js
|
|
7561
|
-
var SCHEMA_GENERATION_RESOURCE2;
|
|
7562
|
-
var init_branch_schema_cache = __esm({
|
|
7563
|
-
"dist/branch-schema-cache.js"() {
|
|
8113
|
+
// dist/config/types.js
|
|
8114
|
+
var primitiveFieldTypes3, fieldTypes3;
|
|
8115
|
+
var init_types5 = __esm({
|
|
8116
|
+
"dist/config/types.js"() {
|
|
7564
8117
|
"use strict";
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
8118
|
+
primitiveFieldTypes3 = [
|
|
8119
|
+
"string",
|
|
8120
|
+
"number",
|
|
8121
|
+
"boolean",
|
|
8122
|
+
"datetime",
|
|
8123
|
+
"rich-text",
|
|
8124
|
+
"markdown",
|
|
8125
|
+
"mdx",
|
|
8126
|
+
"code"
|
|
8127
|
+
];
|
|
8128
|
+
fieldTypes3 = [
|
|
8129
|
+
...primitiveFieldTypes3,
|
|
8130
|
+
"image",
|
|
8131
|
+
"select",
|
|
8132
|
+
"reference",
|
|
8133
|
+
"object",
|
|
8134
|
+
"block",
|
|
8135
|
+
"group"
|
|
8136
|
+
];
|
|
7570
8137
|
}
|
|
7571
8138
|
});
|
|
7572
8139
|
|
|
7573
|
-
// dist/
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
await bumpContentIndexGeneration2(root);
|
|
7579
|
-
await bumpResourceGeneration2(root, SCHEMA_GENERATION_RESOURCE2);
|
|
7580
|
-
invalidateContentIndexesForRoot2(root);
|
|
7581
|
-
}
|
|
7582
|
-
var RESOURCE3;
|
|
7583
|
-
var init_content_index_generation = __esm({
|
|
7584
|
-
"dist/content-index-generation.js"() {
|
|
8140
|
+
// dist/config/schemas/field.js
|
|
8141
|
+
import { z as z9 } from "zod";
|
|
8142
|
+
var fieldBaseSchema3, selectOptionSchema3, referenceOptionSchema3, primitiveFieldSchema3, ASPECT_RATIO_RE3, imageFieldSchema3, selectFieldSchema3, referenceFieldSchema3, fieldHolder3, blockSchema3, blockFieldSchema3, objectFieldSchema3, inlineGroupFieldSchema3, customFieldSchema3, knownFieldSchema3, fieldSchema3;
|
|
8143
|
+
var init_field3 = __esm({
|
|
8144
|
+
"dist/config/schemas/field.js"() {
|
|
7585
8145
|
"use strict";
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
8146
|
+
init_types5();
|
|
8147
|
+
fieldBaseSchema3 = z9.object({
|
|
8148
|
+
name: z9.string().min(1),
|
|
8149
|
+
label: z9.string().optional(),
|
|
8150
|
+
description: z9.string().optional(),
|
|
8151
|
+
required: z9.boolean().optional(),
|
|
8152
|
+
list: z9.boolean().optional(),
|
|
8153
|
+
isTitle: z9.boolean().optional(),
|
|
8154
|
+
isBody: z9.boolean().optional()
|
|
8155
|
+
});
|
|
8156
|
+
selectOptionSchema3 = z9.union([
|
|
8157
|
+
z9.string(),
|
|
8158
|
+
z9.object({
|
|
8159
|
+
label: z9.string().min(1),
|
|
8160
|
+
value: z9.string().min(1)
|
|
8161
|
+
})
|
|
8162
|
+
]);
|
|
8163
|
+
referenceOptionSchema3 = z9.union([
|
|
8164
|
+
z9.string(),
|
|
8165
|
+
z9.object({
|
|
8166
|
+
label: z9.string().min(1),
|
|
8167
|
+
value: z9.string().min(1)
|
|
8168
|
+
})
|
|
8169
|
+
]);
|
|
8170
|
+
primitiveFieldSchema3 = fieldBaseSchema3.extend({
|
|
8171
|
+
type: z9.enum(primitiveFieldTypes3)
|
|
8172
|
+
});
|
|
8173
|
+
ASPECT_RATIO_RE3 = /^[1-9][0-9]*:[1-9][0-9]*$/;
|
|
8174
|
+
imageFieldSchema3 = fieldBaseSchema3.extend({
|
|
8175
|
+
type: z9.literal("image"),
|
|
8176
|
+
aspect: z9.string().regex(ASPECT_RATIO_RE3, 'aspect must be "W:H" with positive integers (e.g. "16:9")').optional(),
|
|
8177
|
+
altOptional: z9.boolean().optional()
|
|
8178
|
+
});
|
|
8179
|
+
selectFieldSchema3 = fieldBaseSchema3.extend({
|
|
8180
|
+
type: z9.literal("select"),
|
|
8181
|
+
options: z9.array(selectOptionSchema3).min(1)
|
|
8182
|
+
});
|
|
8183
|
+
referenceFieldSchema3 = fieldBaseSchema3.extend({
|
|
8184
|
+
type: z9.literal("reference"),
|
|
8185
|
+
collections: z9.array(z9.string().min(1)).min(1).optional(),
|
|
8186
|
+
entryTypes: z9.array(z9.string().min(1)).min(1).optional(),
|
|
8187
|
+
displayField: z9.string().min(1).optional(),
|
|
8188
|
+
options: z9.array(referenceOptionSchema3).optional()
|
|
8189
|
+
});
|
|
8190
|
+
fieldHolder3 = [z9.never()];
|
|
8191
|
+
blockSchema3 = z9.object({
|
|
8192
|
+
name: z9.string().min(1),
|
|
8193
|
+
label: z9.string().optional(),
|
|
8194
|
+
description: z9.string().optional(),
|
|
8195
|
+
fields: z9.array(z9.lazy(() => fieldHolder3[0])).min(1)
|
|
8196
|
+
});
|
|
8197
|
+
blockFieldSchema3 = fieldBaseSchema3.extend({
|
|
8198
|
+
type: z9.literal("block"),
|
|
8199
|
+
templates: z9.array(blockSchema3).min(1)
|
|
8200
|
+
});
|
|
8201
|
+
objectFieldSchema3 = fieldBaseSchema3.extend({
|
|
8202
|
+
type: z9.literal("object"),
|
|
8203
|
+
fields: z9.array(z9.lazy(() => fieldHolder3[0])).min(1)
|
|
8204
|
+
});
|
|
8205
|
+
inlineGroupFieldSchema3 = z9.object({
|
|
8206
|
+
type: z9.literal("group"),
|
|
8207
|
+
name: z9.string().min(1),
|
|
8208
|
+
label: z9.string().optional(),
|
|
8209
|
+
description: z9.string().optional(),
|
|
8210
|
+
fields: z9.array(z9.lazy(() => fieldHolder3[0])).min(1)
|
|
8211
|
+
});
|
|
8212
|
+
customFieldSchema3 = z9.lazy(() => fieldBaseSchema3.extend({
|
|
8213
|
+
type: z9.string().min(1).refine((val) => !fieldTypes3.includes(val), {
|
|
8214
|
+
message: "Custom field types must not conflict with built-in types"
|
|
8215
|
+
})
|
|
8216
|
+
}).passthrough());
|
|
8217
|
+
knownFieldSchema3 = z9.discriminatedUnion("type", [
|
|
8218
|
+
primitiveFieldSchema3,
|
|
8219
|
+
selectFieldSchema3,
|
|
8220
|
+
referenceFieldSchema3,
|
|
8221
|
+
imageFieldSchema3,
|
|
8222
|
+
objectFieldSchema3,
|
|
8223
|
+
blockFieldSchema3,
|
|
8224
|
+
inlineGroupFieldSchema3
|
|
8225
|
+
]);
|
|
8226
|
+
fieldSchema3 = z9.lazy(() => z9.union([knownFieldSchema3, customFieldSchema3]));
|
|
8227
|
+
fieldHolder3[0] = fieldSchema3;
|
|
8228
|
+
}
|
|
8229
|
+
});
|
|
8230
|
+
|
|
8231
|
+
// dist/config/schemas/collection.js
|
|
8232
|
+
import { z as z10 } from "zod";
|
|
8233
|
+
import { isAbsolute as isAbsolute3 } from "pathe";
|
|
8234
|
+
var relativePathSchema3, entryTypeSchema3, collectionSchema3;
|
|
8235
|
+
var init_collection3 = __esm({
|
|
8236
|
+
"dist/config/schemas/collection.js"() {
|
|
8237
|
+
"use strict";
|
|
8238
|
+
init_field3();
|
|
8239
|
+
relativePathSchema3 = z10.string().min(1).refine((val) => !isAbsolute3(val), { message: "Path must be relative" }).refine((val) => !val.split(/[\\/]+/).includes(".."), {
|
|
8240
|
+
message: 'Path must not contain ".."'
|
|
8241
|
+
}).transform((val) => val.split(/[\\/]+/).filter(Boolean).join("/"));
|
|
8242
|
+
entryTypeSchema3 = z10.object({
|
|
8243
|
+
name: z10.string().min(1),
|
|
8244
|
+
format: z10.enum(["md", "mdx", "json", "yaml"]),
|
|
8245
|
+
schema: z10.array(z10.lazy(() => fieldSchema3)).min(1),
|
|
8246
|
+
label: z10.string().optional(),
|
|
8247
|
+
description: z10.string().optional(),
|
|
8248
|
+
default: z10.boolean().optional(),
|
|
8249
|
+
maxItems: z10.number().int().positive().optional()
|
|
8250
|
+
});
|
|
8251
|
+
collectionSchema3 = z10.lazy(() => z10.object({
|
|
8252
|
+
name: z10.string().min(1),
|
|
8253
|
+
path: relativePathSchema3,
|
|
8254
|
+
label: z10.string().optional(),
|
|
8255
|
+
description: z10.string().optional(),
|
|
8256
|
+
entries: z10.array(entryTypeSchema3).optional(),
|
|
8257
|
+
collections: z10.array(collectionSchema3).optional(),
|
|
8258
|
+
order: z10.array(z10.string()).optional()
|
|
8259
|
+
// Embedded IDs for ordering items
|
|
8260
|
+
}).refine((data) => data.entries || data.collections, {
|
|
8261
|
+
message: "Collection must have entries or collections"
|
|
8262
|
+
}));
|
|
8263
|
+
}
|
|
8264
|
+
});
|
|
8265
|
+
|
|
8266
|
+
// dist/config/schemas/media.js
|
|
8267
|
+
import { z as z11 } from "zod";
|
|
8268
|
+
var mediaSchema3;
|
|
8269
|
+
var init_media3 = __esm({
|
|
8270
|
+
"dist/config/schemas/media.js"() {
|
|
8271
|
+
"use strict";
|
|
8272
|
+
init_collection3();
|
|
8273
|
+
mediaSchema3 = z11.discriminatedUnion("adapter", [
|
|
8274
|
+
z11.object({
|
|
8275
|
+
adapter: z11.literal("local"),
|
|
8276
|
+
publicBaseUrl: z11.string().url().optional(),
|
|
8277
|
+
/** Root directory for local asset storage. Defaults to the caller's dev-assets dir. */
|
|
8278
|
+
directory: relativePathSchema3.optional()
|
|
8279
|
+
}),
|
|
8280
|
+
z11.object({
|
|
8281
|
+
adapter: z11.literal("s3"),
|
|
8282
|
+
bucket: z11.string().min(1),
|
|
8283
|
+
region: z11.string().min(1),
|
|
8284
|
+
publicBaseUrl: z11.string().url().optional(),
|
|
8285
|
+
/** Max upload size in bytes for presigned direct uploads. Defaults to 50 MiB. */
|
|
8286
|
+
maxUploadBytes: z11.number().int().positive().optional()
|
|
8287
|
+
}),
|
|
8288
|
+
z11.object({
|
|
8289
|
+
adapter: z11.literal("lfs"),
|
|
8290
|
+
publicBaseUrl: z11.string().url().optional()
|
|
8291
|
+
})
|
|
8292
|
+
]);
|
|
8293
|
+
}
|
|
8294
|
+
});
|
|
8295
|
+
|
|
8296
|
+
// dist/config/schemas/config.js
|
|
8297
|
+
import { z as z12 } from "zod";
|
|
8298
|
+
var defaultBranchAccessSchema3, defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3, defaultPathAccessSchema3, defaultBaseBranchSchema3, defaultRemoteNameSchema3, defaultRemoteUrlSchema3, gitBotAuthorNameSchema3, gitBotAuthorEmailSchema3, githubTokenEnvVarSchema3, operatingModeSchema3, deployedAsSchema3, contentRootSchema3, sourceRootSchema3, deploymentNameSchema3, devContentSyncSchema3, devConfigSchema3, editorConfigSchema3, CanopyConfigSchema3, DEFAULT_PROD_WORKSPACE3;
|
|
8299
|
+
var init_config4 = __esm({
|
|
8300
|
+
"dist/config/schemas/config.js"() {
|
|
8301
|
+
"use strict";
|
|
8302
|
+
init_collection3();
|
|
8303
|
+
init_media3();
|
|
8304
|
+
defaultBranchAccessSchema3 = z12.enum(["allow", "deny"]).default("deny");
|
|
8305
|
+
defaultPathAccessLevelSchema3 = z12.enum(["allow", "deny"]);
|
|
8306
|
+
defaultPathAccessLevelsSchema3 = z12.object({
|
|
8307
|
+
read: defaultPathAccessLevelSchema3.optional(),
|
|
8308
|
+
edit: defaultPathAccessLevelSchema3.optional(),
|
|
8309
|
+
review: defaultPathAccessLevelSchema3.optional()
|
|
8310
|
+
}).strict();
|
|
8311
|
+
defaultPathAccessSchema3 = z12.union([defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3]).default("deny");
|
|
8312
|
+
defaultBaseBranchSchema3 = z12.string().default("main");
|
|
8313
|
+
defaultRemoteNameSchema3 = z12.string().default("origin");
|
|
8314
|
+
defaultRemoteUrlSchema3 = z12.string().min(1);
|
|
8315
|
+
gitBotAuthorNameSchema3 = z12.string().min(1);
|
|
8316
|
+
gitBotAuthorEmailSchema3 = z12.string().email();
|
|
8317
|
+
githubTokenEnvVarSchema3 = z12.string().default("GITHUB_BOT_TOKEN");
|
|
8318
|
+
operatingModeSchema3 = z12.enum(["prod", "dev"]);
|
|
8319
|
+
deployedAsSchema3 = z12.enum(["static", "server"]).default("server");
|
|
8320
|
+
contentRootSchema3 = relativePathSchema3.default("content");
|
|
8321
|
+
sourceRootSchema3 = z12.string().min(1).optional();
|
|
8322
|
+
deploymentNameSchema3 = z12.string().default("prod");
|
|
8323
|
+
devContentSyncSchema3 = z12.enum(["off", "warn"]).default("warn");
|
|
8324
|
+
devConfigSchema3 = z12.object({
|
|
8325
|
+
contentSync: devContentSyncSchema3.optional()
|
|
8326
|
+
});
|
|
8327
|
+
editorConfigSchema3 = z12.object({
|
|
8328
|
+
title: z12.string().optional(),
|
|
8329
|
+
subtitle: z12.string().optional(),
|
|
8330
|
+
theme: z12.unknown().optional(),
|
|
8331
|
+
previewBase: z12.record(z12.string()).optional(),
|
|
8332
|
+
// UI handler functions (runtime only, don't serialize)
|
|
8333
|
+
onAccountClick: z12.function().returns(z12.void()).optional(),
|
|
8334
|
+
onLogoutClick: z12.function().returns(z12.void()).optional(),
|
|
8335
|
+
// Optional: custom account component (e.g., Clerk's UserButton)
|
|
8336
|
+
AccountComponent: z12.custom().optional()
|
|
8337
|
+
});
|
|
8338
|
+
CanopyConfigSchema3 = z12.object({
|
|
8339
|
+
media: mediaSchema3.optional(),
|
|
8340
|
+
// No outer .optional() here: defaultBranchAccessSchema/defaultPathAccessSchema already
|
|
8341
|
+
// have .default('deny'), so the field is optional on input but always resolves to
|
|
8342
|
+
// 'allow'/'deny' (never undefined) on output. An outer .optional() would short-circuit
|
|
8343
|
+
// before the inner default runs, defeating the fail-closed default (SCH-M1).
|
|
8344
|
+
// defaultPathAccessSchema's union keeps the same rule: the .default('deny') sits on the
|
|
8345
|
+
// OUTER union, not inside defaultPathAccessLevelsSchema, so an omitted top-level field
|
|
8346
|
+
// still resolves to 'deny' and an omitted level inside the object form stays undefined
|
|
8347
|
+
// (resolved to 'deny' at read time by resolveDefaultPathAccess).
|
|
8348
|
+
defaultBranchAccess: defaultBranchAccessSchema3,
|
|
8349
|
+
defaultPathAccess: defaultPathAccessSchema3,
|
|
8350
|
+
// .optional() deliberately defeats defaultBaseBranchSchema's .default('main'):
|
|
8351
|
+
// unset must stay undefined after parsing so dev mode can detect the fork
|
|
8352
|
+
// point from git HEAD (see resolveBaseBranch in utils/git.ts).
|
|
8353
|
+
defaultBaseBranch: defaultBaseBranchSchema3.optional(),
|
|
8354
|
+
defaultActiveBranch: z12.string().min(1).optional(),
|
|
8355
|
+
defaultRemoteName: defaultRemoteNameSchema3.optional(),
|
|
8356
|
+
defaultRemoteUrl: defaultRemoteUrlSchema3.optional(),
|
|
8357
|
+
gitBotAuthorName: gitBotAuthorNameSchema3,
|
|
8358
|
+
gitBotAuthorEmail: gitBotAuthorEmailSchema3,
|
|
8359
|
+
githubTokenEnvVar: githubTokenEnvVarSchema3.optional(),
|
|
8360
|
+
// Required by design (follow-up to SEC-C1): a prod deploy that omits `mode` must fail
|
|
8361
|
+
// validation loudly rather than silently running header-trusting dev auth semantics.
|
|
8362
|
+
mode: operatingModeSchema3,
|
|
8363
|
+
deployedAs: deployedAsSchema3,
|
|
8364
|
+
// Has .default('server'), so always present after validation
|
|
8365
|
+
// Escape hatch for prod hosts that genuinely have internet access and intentionally
|
|
8366
|
+
// run git against a network remote (see GitManager.resolveRemoteUrl's prod-mode guard).
|
|
8367
|
+
// Default false/unset — the standard AWS Lambda+worker topology must leave this unset.
|
|
8368
|
+
allowNetworkRemoteInProd: z12.boolean().optional(),
|
|
8369
|
+
settingsBranch: z12.string().optional(),
|
|
8370
|
+
autoCreateSettingsPR: z12.boolean().optional(),
|
|
8371
|
+
// Deliberately `deploymentNameSchema.optional()`, NOT `deploymentNameSchema` alone.
|
|
8372
|
+
// deploymentNameSchema.default('prod') would make `parse(undefined)` resolve to
|
|
8373
|
+
// the literal 'prod' instead of staying `undefined` — collapsing the
|
|
8374
|
+
// env > config > modeDefault precedence chain that resolveDeploymentName
|
|
8375
|
+
// (operating-mode/deployment-name.ts) implements: config would then always
|
|
8376
|
+
// "win" over modeDefault (masking dev's real default of 'local'), and the env
|
|
8377
|
+
// var would just be racing config's baked-in 'prod' instead of a true absence.
|
|
8378
|
+
// `.optional()` here is what keeps an omitted deploymentName reaching
|
|
8379
|
+
// resolveDeploymentName as `undefined`, so it can fall through to modeDefault.
|
|
8380
|
+
// Do not "fix" this by removing `.optional()`.
|
|
8381
|
+
deploymentName: deploymentNameSchema3.optional(),
|
|
8382
|
+
contentRoot: contentRootSchema3.default("content"),
|
|
8383
|
+
sourceRoot: sourceRootSchema3.optional(),
|
|
8384
|
+
editor: editorConfigSchema3.optional(),
|
|
8385
|
+
authPlugin: z12.custom().optional(),
|
|
8386
|
+
entryLinkUrl: z12.custom().optional(),
|
|
8387
|
+
validateEntry: z12.custom().optional(),
|
|
8388
|
+
dev: devConfigSchema3.optional()
|
|
8389
|
+
}).strict();
|
|
8390
|
+
DEFAULT_PROD_WORKSPACE3 = "/mnt/efs/workspace";
|
|
8391
|
+
}
|
|
8392
|
+
});
|
|
8393
|
+
|
|
8394
|
+
// dist/config/validation.js
|
|
8395
|
+
var init_validation4 = __esm({
|
|
8396
|
+
"dist/config/validation.js"() {
|
|
8397
|
+
"use strict";
|
|
8398
|
+
init_config4();
|
|
8399
|
+
init_flatten3();
|
|
8400
|
+
}
|
|
8401
|
+
});
|
|
8402
|
+
|
|
8403
|
+
// dist/validation/entry-type-reference-validator.js
|
|
8404
|
+
var init_entry_type_reference_validator = __esm({
|
|
8405
|
+
"dist/validation/entry-type-reference-validator.js"() {
|
|
8406
|
+
"use strict";
|
|
8407
|
+
init_validation4();
|
|
8408
|
+
}
|
|
8409
|
+
});
|
|
8410
|
+
|
|
8411
|
+
// dist/build-mode.js
|
|
8412
|
+
var STATIC_DEPLOY_USER2;
|
|
8413
|
+
var init_build_mode = __esm({
|
|
8414
|
+
"dist/build-mode.js"() {
|
|
8415
|
+
"use strict";
|
|
8416
|
+
STATIC_DEPLOY_USER2 = Object.freeze({
|
|
8417
|
+
type: "authenticated",
|
|
8418
|
+
userId: "__static_deploy__",
|
|
8419
|
+
groups: ["Admins"],
|
|
8420
|
+
email: "static-deploy@canopycms",
|
|
8421
|
+
name: "Static Deploy"
|
|
8422
|
+
});
|
|
8423
|
+
}
|
|
8424
|
+
});
|
|
8425
|
+
|
|
8426
|
+
// dist/branch-schema-cache.js
|
|
8427
|
+
var SCHEMA_GENERATION_RESOURCE2;
|
|
8428
|
+
var init_branch_schema_cache = __esm({
|
|
8429
|
+
"dist/branch-schema-cache.js"() {
|
|
8430
|
+
"use strict";
|
|
8431
|
+
init_resolver();
|
|
8432
|
+
init_flatten3();
|
|
8433
|
+
init_entry_type_reference_validator();
|
|
8434
|
+
init_build_mode();
|
|
8435
|
+
init_resource_generation();
|
|
8436
|
+
SCHEMA_GENERATION_RESOURCE2 = "schema";
|
|
8437
|
+
}
|
|
8438
|
+
});
|
|
8439
|
+
|
|
8440
|
+
// dist/content-index-generation.js
|
|
8441
|
+
async function bumpContentIndexGeneration2(root) {
|
|
8442
|
+
return bumpResourceGeneration2(root, RESOURCE3);
|
|
8443
|
+
}
|
|
8444
|
+
async function invalidateBranchContentCaches2(root) {
|
|
8445
|
+
await bumpContentIndexGeneration2(root);
|
|
8446
|
+
await bumpResourceGeneration2(root, SCHEMA_GENERATION_RESOURCE2);
|
|
8447
|
+
invalidateContentIndexesForRoot2(root);
|
|
8448
|
+
}
|
|
8449
|
+
var RESOURCE3;
|
|
8450
|
+
var init_content_index_generation = __esm({
|
|
8451
|
+
"dist/content-index-generation.js"() {
|
|
8452
|
+
"use strict";
|
|
8453
|
+
init_resource_generation();
|
|
8454
|
+
init_content_index_registry();
|
|
8455
|
+
init_branch_schema_cache();
|
|
8456
|
+
RESOURCE3 = "content-index";
|
|
8457
|
+
}
|
|
8458
|
+
});
|
|
8459
|
+
|
|
8460
|
+
// dist/sync-core.js
|
|
8461
|
+
import fs20 from "node:fs/promises";
|
|
8462
|
+
import path23 from "node:path";
|
|
8463
|
+
import { simpleGit as simpleGit4 } from "simple-git";
|
|
8464
|
+
function assertWithinDir(resolved, parent, label) {
|
|
8465
|
+
const normalizedResolved = path23.resolve(resolved);
|
|
8466
|
+
const normalizedParent = path23.resolve(parent);
|
|
8467
|
+
if (!normalizedResolved.startsWith(normalizedParent + path23.sep) && normalizedResolved !== normalizedParent) {
|
|
8468
|
+
throw new Error(`${label} escapes the expected directory: ${resolved}`);
|
|
8469
|
+
}
|
|
8470
|
+
}
|
|
8471
|
+
async function safeReplaceDir(oldDir, newDir) {
|
|
8472
|
+
const backupDir = `${oldDir}.sync-backup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
8473
|
+
const oldExists = await filePathExists(oldDir);
|
|
8474
|
+
if (oldExists) {
|
|
8475
|
+
await fs20.rename(oldDir, backupDir);
|
|
8476
|
+
}
|
|
8477
|
+
try {
|
|
8478
|
+
await fs20.rename(newDir, oldDir);
|
|
8479
|
+
} catch (err) {
|
|
8480
|
+
if (oldExists) {
|
|
8481
|
+
await fs20.rename(backupDir, oldDir).catch(() => {
|
|
8482
|
+
});
|
|
8483
|
+
}
|
|
8484
|
+
throw err;
|
|
8485
|
+
}
|
|
8486
|
+
if (oldExists) {
|
|
8487
|
+
await fs20.rm(backupDir, { recursive: true, force: true }).catch(() => {
|
|
8488
|
+
});
|
|
8489
|
+
}
|
|
8490
|
+
}
|
|
8491
|
+
async function listFilesRecursive(dir, prefix = "") {
|
|
8492
|
+
const results = [];
|
|
7626
8493
|
let entries;
|
|
7627
8494
|
try {
|
|
7628
8495
|
entries = await fs20.readdir(dir, { withFileTypes: true });
|
|
@@ -7677,7 +8544,7 @@ async function pushContentToWorkspace(options) {
|
|
|
7677
8544
|
throw err;
|
|
7678
8545
|
}
|
|
7679
8546
|
try {
|
|
7680
|
-
const wsGit =
|
|
8547
|
+
const wsGit = simpleGit4({ baseDir: branchPath });
|
|
7681
8548
|
await wsGit.add("-A");
|
|
7682
8549
|
const postStatus = await wsGit.status();
|
|
7683
8550
|
if (postStatus.files.length === 0) {
|
|
@@ -7704,7 +8571,7 @@ var init_sync_core = __esm({
|
|
|
7704
8571
|
});
|
|
7705
8572
|
|
|
7706
8573
|
// dist/utils/git.js
|
|
7707
|
-
import { simpleGit as
|
|
8574
|
+
import { simpleGit as simpleGit5 } from "simple-git";
|
|
7708
8575
|
var init_git = __esm({
|
|
7709
8576
|
"dist/utils/git.js"() {
|
|
7710
8577
|
"use strict";
|
|
@@ -7722,379 +8589,134 @@ var init_provisioning_lock = __esm({
|
|
|
7722
8589
|
// dist/git-manager.js
|
|
7723
8590
|
import fs21 from "node:fs/promises";
|
|
7724
8591
|
import path24 from "node:path";
|
|
7725
|
-
import { simpleGit as
|
|
8592
|
+
import { simpleGit as simpleGit6 } from "simple-git";
|
|
7726
8593
|
async function ensureGitExcludePattern2(repoPath, pattern) {
|
|
7727
|
-
const excludePath = path24.join(repoPath, ".git", "info", "exclude");
|
|
7728
|
-
await fs21.mkdir(path24.dirname(excludePath), { recursive: true });
|
|
7729
|
-
let content = "";
|
|
7730
|
-
try {
|
|
7731
|
-
content = await fs21.readFile(excludePath, "utf-8");
|
|
7732
|
-
} catch (err) {
|
|
7733
|
-
if (!isNotFoundError(err))
|
|
7734
|
-
throw err;
|
|
7735
|
-
}
|
|
7736
|
-
const lines = content.split("\n");
|
|
7737
|
-
if (lines.some((line) => line.trim() === pattern)) {
|
|
7738
|
-
log9.debug("git", "Pattern already in .git/info/exclude", { pattern });
|
|
7739
|
-
return;
|
|
7740
|
-
}
|
|
7741
|
-
const needsLeadingNewline = content.length > 0 && !content.endsWith("\n");
|
|
7742
|
-
const newContent = content + (needsLeadingNewline ? "\n" : "") + pattern + "\n";
|
|
7743
|
-
await fs21.writeFile(excludePath, newContent, "utf-8");
|
|
7744
|
-
log9.debug("git", "Added pattern to .git/info/exclude", { pattern });
|
|
7745
|
-
}
|
|
7746
|
-
var log9;
|
|
7747
|
-
var init_git_manager = __esm({
|
|
7748
|
-
"dist/git-manager.js"() {
|
|
7749
|
-
"use strict";
|
|
7750
|
-
init_content_index_registry();
|
|
7751
|
-
init_content_index_generation();
|
|
7752
|
-
init_debug2();
|
|
7753
|
-
init_error();
|
|
7754
|
-
init_git();
|
|
7755
|
-
init_provisioning_lock();
|
|
7756
|
-
log9 = createDebugLogger3({ prefix: "GitManager" });
|
|
7757
|
-
}
|
|
7758
|
-
});
|
|
7759
|
-
|
|
7760
|
-
// dist/operating-mode/client-safe-strategy.js
|
|
7761
|
-
var ProdClientSafeStrategy3, DevClientSafeStrategy3;
|
|
7762
|
-
var init_client_safe_strategy2 = __esm({
|
|
7763
|
-
"dist/operating-mode/client-safe-strategy.js"() {
|
|
7764
|
-
"use strict";
|
|
7765
|
-
ProdClientSafeStrategy3 = class {
|
|
7766
|
-
constructor() {
|
|
7767
|
-
this.mode = "prod";
|
|
7768
|
-
}
|
|
7769
|
-
// UI Feature Flags
|
|
7770
|
-
supportsBranching() {
|
|
7771
|
-
return true;
|
|
7772
|
-
}
|
|
7773
|
-
supportsStatusBadge() {
|
|
7774
|
-
return true;
|
|
7775
|
-
}
|
|
7776
|
-
supportsComments() {
|
|
7777
|
-
return true;
|
|
7778
|
-
}
|
|
7779
|
-
supportsPullRequests() {
|
|
7780
|
-
return true;
|
|
7781
|
-
}
|
|
7782
|
-
// Simple Data
|
|
7783
|
-
getPermissionsFileName() {
|
|
7784
|
-
return "permissions.json";
|
|
7785
|
-
}
|
|
7786
|
-
getGroupsFileName() {
|
|
7787
|
-
return "groups.json";
|
|
7788
|
-
}
|
|
7789
|
-
shouldCommit() {
|
|
7790
|
-
return true;
|
|
7791
|
-
}
|
|
7792
|
-
shouldPush() {
|
|
7793
|
-
return true;
|
|
7794
|
-
}
|
|
7795
|
-
};
|
|
7796
|
-
DevClientSafeStrategy3 = class {
|
|
7797
|
-
constructor() {
|
|
7798
|
-
this.mode = "dev";
|
|
7799
|
-
}
|
|
7800
|
-
// UI Feature Flags
|
|
7801
|
-
supportsBranching() {
|
|
7802
|
-
return true;
|
|
7803
|
-
}
|
|
7804
|
-
supportsStatusBadge() {
|
|
7805
|
-
return true;
|
|
7806
|
-
}
|
|
7807
|
-
supportsComments() {
|
|
7808
|
-
return true;
|
|
7809
|
-
}
|
|
7810
|
-
supportsPullRequests() {
|
|
7811
|
-
return false;
|
|
7812
|
-
}
|
|
7813
|
-
// Simple Data
|
|
7814
|
-
getPermissionsFileName() {
|
|
7815
|
-
return "permissions.json";
|
|
7816
|
-
}
|
|
7817
|
-
getGroupsFileName() {
|
|
7818
|
-
return "groups.json";
|
|
7819
|
-
}
|
|
7820
|
-
shouldCommit() {
|
|
7821
|
-
return true;
|
|
7822
|
-
}
|
|
7823
|
-
shouldPush() {
|
|
7824
|
-
return true;
|
|
7825
|
-
}
|
|
7826
|
-
};
|
|
7827
|
-
}
|
|
7828
|
-
});
|
|
7829
|
-
|
|
7830
|
-
// dist/config/types.js
|
|
7831
|
-
var primitiveFieldTypes3, fieldTypes3;
|
|
7832
|
-
var init_types5 = __esm({
|
|
7833
|
-
"dist/config/types.js"() {
|
|
7834
|
-
"use strict";
|
|
7835
|
-
primitiveFieldTypes3 = [
|
|
7836
|
-
"string",
|
|
7837
|
-
"number",
|
|
7838
|
-
"boolean",
|
|
7839
|
-
"datetime",
|
|
7840
|
-
"rich-text",
|
|
7841
|
-
"markdown",
|
|
7842
|
-
"mdx",
|
|
7843
|
-
"code"
|
|
7844
|
-
];
|
|
7845
|
-
fieldTypes3 = [
|
|
7846
|
-
...primitiveFieldTypes3,
|
|
7847
|
-
"image",
|
|
7848
|
-
"select",
|
|
7849
|
-
"reference",
|
|
7850
|
-
"object",
|
|
7851
|
-
"block",
|
|
7852
|
-
"group"
|
|
7853
|
-
];
|
|
7854
|
-
}
|
|
7855
|
-
});
|
|
7856
|
-
|
|
7857
|
-
// dist/config/schemas/field.js
|
|
7858
|
-
import { z as z9 } from "zod";
|
|
7859
|
-
var fieldBaseSchema3, selectOptionSchema3, referenceOptionSchema3, primitiveFieldSchema3, ASPECT_RATIO_RE3, imageFieldSchema3, selectFieldSchema3, referenceFieldSchema3, fieldHolder3, blockSchema3, blockFieldSchema3, objectFieldSchema3, inlineGroupFieldSchema3, customFieldSchema3, knownFieldSchema3, fieldSchema3;
|
|
7860
|
-
var init_field3 = __esm({
|
|
7861
|
-
"dist/config/schemas/field.js"() {
|
|
7862
|
-
"use strict";
|
|
7863
|
-
init_types5();
|
|
7864
|
-
fieldBaseSchema3 = z9.object({
|
|
7865
|
-
name: z9.string().min(1),
|
|
7866
|
-
label: z9.string().optional(),
|
|
7867
|
-
description: z9.string().optional(),
|
|
7868
|
-
required: z9.boolean().optional(),
|
|
7869
|
-
list: z9.boolean().optional(),
|
|
7870
|
-
isTitle: z9.boolean().optional(),
|
|
7871
|
-
isBody: z9.boolean().optional()
|
|
7872
|
-
});
|
|
7873
|
-
selectOptionSchema3 = z9.union([
|
|
7874
|
-
z9.string(),
|
|
7875
|
-
z9.object({
|
|
7876
|
-
label: z9.string().min(1),
|
|
7877
|
-
value: z9.string().min(1)
|
|
7878
|
-
})
|
|
7879
|
-
]);
|
|
7880
|
-
referenceOptionSchema3 = z9.union([
|
|
7881
|
-
z9.string(),
|
|
7882
|
-
z9.object({
|
|
7883
|
-
label: z9.string().min(1),
|
|
7884
|
-
value: z9.string().min(1)
|
|
7885
|
-
})
|
|
7886
|
-
]);
|
|
7887
|
-
primitiveFieldSchema3 = fieldBaseSchema3.extend({
|
|
7888
|
-
type: z9.enum(primitiveFieldTypes3)
|
|
7889
|
-
});
|
|
7890
|
-
ASPECT_RATIO_RE3 = /^[1-9][0-9]*:[1-9][0-9]*$/;
|
|
7891
|
-
imageFieldSchema3 = fieldBaseSchema3.extend({
|
|
7892
|
-
type: z9.literal("image"),
|
|
7893
|
-
aspect: z9.string().regex(ASPECT_RATIO_RE3, 'aspect must be "W:H" with positive integers (e.g. "16:9")').optional(),
|
|
7894
|
-
altOptional: z9.boolean().optional()
|
|
7895
|
-
});
|
|
7896
|
-
selectFieldSchema3 = fieldBaseSchema3.extend({
|
|
7897
|
-
type: z9.literal("select"),
|
|
7898
|
-
options: z9.array(selectOptionSchema3).min(1)
|
|
7899
|
-
});
|
|
7900
|
-
referenceFieldSchema3 = fieldBaseSchema3.extend({
|
|
7901
|
-
type: z9.literal("reference"),
|
|
7902
|
-
collections: z9.array(z9.string().min(1)).min(1).optional(),
|
|
7903
|
-
entryTypes: z9.array(z9.string().min(1)).min(1).optional(),
|
|
7904
|
-
displayField: z9.string().min(1).optional(),
|
|
7905
|
-
options: z9.array(referenceOptionSchema3).optional()
|
|
7906
|
-
});
|
|
7907
|
-
fieldHolder3 = [z9.never()];
|
|
7908
|
-
blockSchema3 = z9.object({
|
|
7909
|
-
name: z9.string().min(1),
|
|
7910
|
-
label: z9.string().optional(),
|
|
7911
|
-
description: z9.string().optional(),
|
|
7912
|
-
fields: z9.array(z9.lazy(() => fieldHolder3[0])).min(1)
|
|
7913
|
-
});
|
|
7914
|
-
blockFieldSchema3 = fieldBaseSchema3.extend({
|
|
7915
|
-
type: z9.literal("block"),
|
|
7916
|
-
templates: z9.array(blockSchema3).min(1)
|
|
7917
|
-
});
|
|
7918
|
-
objectFieldSchema3 = fieldBaseSchema3.extend({
|
|
7919
|
-
type: z9.literal("object"),
|
|
7920
|
-
fields: z9.array(z9.lazy(() => fieldHolder3[0])).min(1)
|
|
7921
|
-
});
|
|
7922
|
-
inlineGroupFieldSchema3 = z9.object({
|
|
7923
|
-
type: z9.literal("group"),
|
|
7924
|
-
name: z9.string().min(1),
|
|
7925
|
-
label: z9.string().optional(),
|
|
7926
|
-
description: z9.string().optional(),
|
|
7927
|
-
fields: z9.array(z9.lazy(() => fieldHolder3[0])).min(1)
|
|
7928
|
-
});
|
|
7929
|
-
customFieldSchema3 = z9.lazy(() => fieldBaseSchema3.extend({
|
|
7930
|
-
type: z9.string().min(1).refine((val) => !fieldTypes3.includes(val), {
|
|
7931
|
-
message: "Custom field types must not conflict with built-in types"
|
|
7932
|
-
})
|
|
7933
|
-
}).passthrough());
|
|
7934
|
-
knownFieldSchema3 = z9.discriminatedUnion("type", [
|
|
7935
|
-
primitiveFieldSchema3,
|
|
7936
|
-
selectFieldSchema3,
|
|
7937
|
-
referenceFieldSchema3,
|
|
7938
|
-
imageFieldSchema3,
|
|
7939
|
-
objectFieldSchema3,
|
|
7940
|
-
blockFieldSchema3,
|
|
7941
|
-
inlineGroupFieldSchema3
|
|
7942
|
-
]);
|
|
7943
|
-
fieldSchema3 = z9.lazy(() => z9.union([knownFieldSchema3, customFieldSchema3]));
|
|
7944
|
-
fieldHolder3[0] = fieldSchema3;
|
|
8594
|
+
const excludePath = path24.join(repoPath, ".git", "info", "exclude");
|
|
8595
|
+
await fs21.mkdir(path24.dirname(excludePath), { recursive: true });
|
|
8596
|
+
let content = "";
|
|
8597
|
+
try {
|
|
8598
|
+
content = await fs21.readFile(excludePath, "utf-8");
|
|
8599
|
+
} catch (err) {
|
|
8600
|
+
if (!isNotFoundError(err))
|
|
8601
|
+
throw err;
|
|
7945
8602
|
}
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
|
|
8603
|
+
const lines = content.split("\n");
|
|
8604
|
+
if (lines.some((line) => line.trim() === pattern)) {
|
|
8605
|
+
log9.debug("git", "Pattern already in .git/info/exclude", { pattern });
|
|
8606
|
+
return;
|
|
8607
|
+
}
|
|
8608
|
+
const needsLeadingNewline = content.length > 0 && !content.endsWith("\n");
|
|
8609
|
+
const newContent = content + (needsLeadingNewline ? "\n" : "") + pattern + "\n";
|
|
8610
|
+
await fs21.writeFile(excludePath, newContent, "utf-8");
|
|
8611
|
+
log9.debug("git", "Added pattern to .git/info/exclude", { pattern });
|
|
8612
|
+
}
|
|
8613
|
+
var log9;
|
|
8614
|
+
var init_git_manager = __esm({
|
|
8615
|
+
"dist/git-manager.js"() {
|
|
7954
8616
|
"use strict";
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
schema: z10.array(z10.lazy(() => fieldSchema3)).min(1),
|
|
7963
|
-
label: z10.string().optional(),
|
|
7964
|
-
description: z10.string().optional(),
|
|
7965
|
-
default: z10.boolean().optional(),
|
|
7966
|
-
maxItems: z10.number().int().positive().optional()
|
|
7967
|
-
});
|
|
7968
|
-
collectionSchema3 = z10.lazy(() => z10.object({
|
|
7969
|
-
name: z10.string().min(1),
|
|
7970
|
-
path: relativePathSchema3,
|
|
7971
|
-
label: z10.string().optional(),
|
|
7972
|
-
description: z10.string().optional(),
|
|
7973
|
-
entries: z10.array(entryTypeSchema3).optional(),
|
|
7974
|
-
collections: z10.array(collectionSchema3).optional(),
|
|
7975
|
-
order: z10.array(z10.string()).optional()
|
|
7976
|
-
// Embedded IDs for ordering items
|
|
7977
|
-
}).refine((data) => data.entries || data.collections, {
|
|
7978
|
-
message: "Collection must have entries or collections"
|
|
7979
|
-
}));
|
|
8617
|
+
init_content_index_registry();
|
|
8618
|
+
init_content_index_generation();
|
|
8619
|
+
init_debug2();
|
|
8620
|
+
init_error();
|
|
8621
|
+
init_git();
|
|
8622
|
+
init_provisioning_lock();
|
|
8623
|
+
log9 = createDebugLogger3({ prefix: "GitManager" });
|
|
7980
8624
|
}
|
|
7981
8625
|
});
|
|
7982
8626
|
|
|
7983
|
-
// dist/
|
|
7984
|
-
|
|
7985
|
-
var
|
|
7986
|
-
|
|
7987
|
-
"dist/config/schemas/media.js"() {
|
|
8627
|
+
// dist/operating-mode/client-safe-strategy.js
|
|
8628
|
+
var ProdClientSafeStrategy3, DevClientSafeStrategy3;
|
|
8629
|
+
var init_client_safe_strategy2 = __esm({
|
|
8630
|
+
"dist/operating-mode/client-safe-strategy.js"() {
|
|
7988
8631
|
"use strict";
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
}
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8632
|
+
ProdClientSafeStrategy3 = class {
|
|
8633
|
+
constructor() {
|
|
8634
|
+
this.mode = "prod";
|
|
8635
|
+
}
|
|
8636
|
+
// UI Feature Flags
|
|
8637
|
+
supportsBranching() {
|
|
8638
|
+
return true;
|
|
8639
|
+
}
|
|
8640
|
+
supportsStatusBadge() {
|
|
8641
|
+
return true;
|
|
8642
|
+
}
|
|
8643
|
+
supportsComments() {
|
|
8644
|
+
return true;
|
|
8645
|
+
}
|
|
8646
|
+
supportsPullRequests() {
|
|
8647
|
+
return true;
|
|
8648
|
+
}
|
|
8649
|
+
// Simple Data
|
|
8650
|
+
getPermissionsFileName() {
|
|
8651
|
+
return "permissions.json";
|
|
8652
|
+
}
|
|
8653
|
+
getGroupsFileName() {
|
|
8654
|
+
return "groups.json";
|
|
8655
|
+
}
|
|
8656
|
+
shouldCommit() {
|
|
8657
|
+
return true;
|
|
8658
|
+
}
|
|
8659
|
+
shouldPush() {
|
|
8660
|
+
return true;
|
|
8661
|
+
}
|
|
8662
|
+
};
|
|
8663
|
+
DevClientSafeStrategy3 = class {
|
|
8664
|
+
constructor() {
|
|
8665
|
+
this.mode = "dev";
|
|
8666
|
+
}
|
|
8667
|
+
// UI Feature Flags
|
|
8668
|
+
supportsBranching() {
|
|
8669
|
+
return true;
|
|
8670
|
+
}
|
|
8671
|
+
supportsStatusBadge() {
|
|
8672
|
+
return true;
|
|
8673
|
+
}
|
|
8674
|
+
supportsComments() {
|
|
8675
|
+
return true;
|
|
8676
|
+
}
|
|
8677
|
+
supportsPullRequests() {
|
|
8678
|
+
return false;
|
|
8679
|
+
}
|
|
8680
|
+
// Simple Data
|
|
8681
|
+
getPermissionsFileName() {
|
|
8682
|
+
return "permissions.json";
|
|
8683
|
+
}
|
|
8684
|
+
getGroupsFileName() {
|
|
8685
|
+
return "groups.json";
|
|
8686
|
+
}
|
|
8687
|
+
shouldCommit() {
|
|
8688
|
+
return true;
|
|
8689
|
+
}
|
|
8690
|
+
shouldPush() {
|
|
8691
|
+
return true;
|
|
8692
|
+
}
|
|
8693
|
+
};
|
|
8010
8694
|
}
|
|
8011
8695
|
});
|
|
8012
8696
|
|
|
8013
|
-
// dist/
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8697
|
+
// dist/operating-mode/deployment-name.js
|
|
8698
|
+
function resolveDeploymentName3(config, modeDefault) {
|
|
8699
|
+
const envValue = process.env.CANOPYCMS_DEPLOYMENT_NAME?.trim();
|
|
8700
|
+
const configValue = config.deploymentName;
|
|
8701
|
+
if (envValue && configValue && envValue !== configValue && !warned3) {
|
|
8702
|
+
canopyLogWarn3(`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.`);
|
|
8703
|
+
warned3 = true;
|
|
8704
|
+
}
|
|
8705
|
+
const resolved = envValue || configValue || modeDefault;
|
|
8706
|
+
if (!isValidDeploymentName3(resolved)) {
|
|
8707
|
+
const source = envValue ? "CANOPYCMS_DEPLOYMENT_NAME" : configValue ? "config.deploymentName" : "the mode default";
|
|
8708
|
+
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'.`);
|
|
8709
|
+
}
|
|
8710
|
+
return resolved;
|
|
8711
|
+
}
|
|
8712
|
+
var warned3, VALID_DEPLOYMENT_NAME3, isValidDeploymentName3;
|
|
8713
|
+
var init_deployment_name2 = __esm({
|
|
8714
|
+
"dist/operating-mode/deployment-name.js"() {
|
|
8018
8715
|
"use strict";
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
defaultPathAccessLevelsSchema3 = z12.object({
|
|
8024
|
-
read: defaultPathAccessLevelSchema3.optional(),
|
|
8025
|
-
edit: defaultPathAccessLevelSchema3.optional(),
|
|
8026
|
-
review: defaultPathAccessLevelSchema3.optional()
|
|
8027
|
-
}).strict();
|
|
8028
|
-
defaultPathAccessSchema3 = z12.union([defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3]).default("deny");
|
|
8029
|
-
defaultBaseBranchSchema3 = z12.string().default("main");
|
|
8030
|
-
defaultRemoteNameSchema3 = z12.string().default("origin");
|
|
8031
|
-
defaultRemoteUrlSchema3 = z12.string().min(1);
|
|
8032
|
-
gitBotAuthorNameSchema3 = z12.string().min(1);
|
|
8033
|
-
gitBotAuthorEmailSchema3 = z12.string().email();
|
|
8034
|
-
githubTokenEnvVarSchema3 = z12.string().default("GITHUB_BOT_TOKEN");
|
|
8035
|
-
operatingModeSchema3 = z12.enum(["prod", "dev"]);
|
|
8036
|
-
deployedAsSchema3 = z12.enum(["static", "server"]).default("server");
|
|
8037
|
-
contentRootSchema3 = relativePathSchema3.default("content");
|
|
8038
|
-
sourceRootSchema3 = z12.string().min(1).optional();
|
|
8039
|
-
deploymentNameSchema3 = z12.string().default("prod");
|
|
8040
|
-
devContentSyncSchema3 = z12.enum(["off", "warn"]).default("warn");
|
|
8041
|
-
devConfigSchema3 = z12.object({
|
|
8042
|
-
contentSync: devContentSyncSchema3.optional()
|
|
8043
|
-
});
|
|
8044
|
-
editorConfigSchema3 = z12.object({
|
|
8045
|
-
title: z12.string().optional(),
|
|
8046
|
-
subtitle: z12.string().optional(),
|
|
8047
|
-
theme: z12.unknown().optional(),
|
|
8048
|
-
previewBase: z12.record(z12.string()).optional(),
|
|
8049
|
-
// UI handler functions (runtime only, don't serialize)
|
|
8050
|
-
onAccountClick: z12.function().returns(z12.void()).optional(),
|
|
8051
|
-
onLogoutClick: z12.function().returns(z12.void()).optional(),
|
|
8052
|
-
// Optional: custom account component (e.g., Clerk's UserButton)
|
|
8053
|
-
AccountComponent: z12.custom().optional()
|
|
8054
|
-
});
|
|
8055
|
-
CanopyConfigSchema3 = z12.object({
|
|
8056
|
-
media: mediaSchema3.optional(),
|
|
8057
|
-
// No outer .optional() here: defaultBranchAccessSchema/defaultPathAccessSchema already
|
|
8058
|
-
// have .default('deny'), so the field is optional on input but always resolves to
|
|
8059
|
-
// 'allow'/'deny' (never undefined) on output. An outer .optional() would short-circuit
|
|
8060
|
-
// before the inner default runs, defeating the fail-closed default (SCH-M1).
|
|
8061
|
-
// defaultPathAccessSchema's union keeps the same rule: the .default('deny') sits on the
|
|
8062
|
-
// OUTER union, not inside defaultPathAccessLevelsSchema, so an omitted top-level field
|
|
8063
|
-
// still resolves to 'deny' and an omitted level inside the object form stays undefined
|
|
8064
|
-
// (resolved to 'deny' at read time by resolveDefaultPathAccess).
|
|
8065
|
-
defaultBranchAccess: defaultBranchAccessSchema3,
|
|
8066
|
-
defaultPathAccess: defaultPathAccessSchema3,
|
|
8067
|
-
// .optional() deliberately defeats defaultBaseBranchSchema's .default('main'):
|
|
8068
|
-
// unset must stay undefined after parsing so dev mode can detect the fork
|
|
8069
|
-
// point from git HEAD (see resolveBaseBranch in utils/git.ts).
|
|
8070
|
-
defaultBaseBranch: defaultBaseBranchSchema3.optional(),
|
|
8071
|
-
defaultActiveBranch: z12.string().min(1).optional(),
|
|
8072
|
-
defaultRemoteName: defaultRemoteNameSchema3.optional(),
|
|
8073
|
-
defaultRemoteUrl: defaultRemoteUrlSchema3.optional(),
|
|
8074
|
-
gitBotAuthorName: gitBotAuthorNameSchema3,
|
|
8075
|
-
gitBotAuthorEmail: gitBotAuthorEmailSchema3,
|
|
8076
|
-
githubTokenEnvVar: githubTokenEnvVarSchema3.optional(),
|
|
8077
|
-
// Required by design (follow-up to SEC-C1): a prod deploy that omits `mode` must fail
|
|
8078
|
-
// validation loudly rather than silently running header-trusting dev auth semantics.
|
|
8079
|
-
mode: operatingModeSchema3,
|
|
8080
|
-
deployedAs: deployedAsSchema3,
|
|
8081
|
-
// Has .default('server'), so always present after validation
|
|
8082
|
-
// Escape hatch for prod hosts that genuinely have internet access and intentionally
|
|
8083
|
-
// run git against a network remote (see GitManager.resolveRemoteUrl's prod-mode guard).
|
|
8084
|
-
// Default false/unset — the standard AWS Lambda+worker topology must leave this unset.
|
|
8085
|
-
allowNetworkRemoteInProd: z12.boolean().optional(),
|
|
8086
|
-
settingsBranch: z12.string().optional(),
|
|
8087
|
-
autoCreateSettingsPR: z12.boolean().optional(),
|
|
8088
|
-
deploymentName: deploymentNameSchema3.optional(),
|
|
8089
|
-
contentRoot: contentRootSchema3.default("content"),
|
|
8090
|
-
sourceRoot: sourceRootSchema3.optional(),
|
|
8091
|
-
editor: editorConfigSchema3.optional(),
|
|
8092
|
-
authPlugin: z12.custom().optional(),
|
|
8093
|
-
entryLinkUrl: z12.custom().optional(),
|
|
8094
|
-
validateEntry: z12.custom().optional(),
|
|
8095
|
-
dev: devConfigSchema3.optional()
|
|
8096
|
-
}).strict();
|
|
8097
|
-
DEFAULT_PROD_WORKSPACE3 = "/mnt/efs/workspace";
|
|
8716
|
+
init_logger2();
|
|
8717
|
+
warned3 = false;
|
|
8718
|
+
VALID_DEPLOYMENT_NAME3 = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
8719
|
+
isValidDeploymentName3 = (name) => VALID_DEPLOYMENT_NAME3.test(name) && !name.includes("..") && !name.endsWith(".") && !name.endsWith(".lock");
|
|
8098
8720
|
}
|
|
8099
8721
|
});
|
|
8100
8722
|
|
|
@@ -8117,15 +8739,6 @@ var init_permissions3 = __esm({
|
|
|
8117
8739
|
}
|
|
8118
8740
|
});
|
|
8119
8741
|
|
|
8120
|
-
// dist/config/validation.js
|
|
8121
|
-
var init_validation4 = __esm({
|
|
8122
|
-
"dist/config/validation.js"() {
|
|
8123
|
-
"use strict";
|
|
8124
|
-
init_config4();
|
|
8125
|
-
init_flatten3();
|
|
8126
|
-
}
|
|
8127
|
-
});
|
|
8128
|
-
|
|
8129
8742
|
// dist/config/helpers.js
|
|
8130
8743
|
var init_helpers3 = __esm({
|
|
8131
8744
|
"dist/config/helpers.js"() {
|
|
@@ -8177,6 +8790,7 @@ var init_client_unsafe_strategy2 = __esm({
|
|
|
8177
8790
|
"dist/operating-mode/client-unsafe-strategy.js"() {
|
|
8178
8791
|
"use strict";
|
|
8179
8792
|
init_client_safe_strategy2();
|
|
8793
|
+
init_deployment_name2();
|
|
8180
8794
|
init_config5();
|
|
8181
8795
|
ProdStrategy3 = class extends ProdClientSafeStrategy3 {
|
|
8182
8796
|
// All client-safe methods inherited automatically from ProdClientSafeStrategy:
|
|
@@ -8187,8 +8801,8 @@ var init_client_unsafe_strategy2 = __esm({
|
|
|
8187
8801
|
getWorkspaceRoot(_sourceRoot) {
|
|
8188
8802
|
return path25.resolve(process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE3);
|
|
8189
8803
|
}
|
|
8190
|
-
getContentRoot(sourceRoot) {
|
|
8191
|
-
return path25.resolve(sourceRoot ?? process.cwd(),
|
|
8804
|
+
getContentRoot(contentRoot, sourceRoot) {
|
|
8805
|
+
return path25.resolve(sourceRoot ?? process.cwd(), contentRoot);
|
|
8192
8806
|
}
|
|
8193
8807
|
getContentBranchesRoot(sourceRoot) {
|
|
8194
8808
|
return path25.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
|
|
@@ -8219,8 +8833,7 @@ var init_client_unsafe_strategy2 = __esm({
|
|
|
8219
8833
|
getSettingsBranchName(config) {
|
|
8220
8834
|
if (config.settingsBranch)
|
|
8221
8835
|
return config.settingsBranch;
|
|
8222
|
-
|
|
8223
|
-
return `canopycms-settings-${deploymentName}`;
|
|
8836
|
+
return `canopycms-settings-${resolveDeploymentName3(config, "prod")}`;
|
|
8224
8837
|
}
|
|
8225
8838
|
getSettingsRoot(sourceRoot) {
|
|
8226
8839
|
return path25.join(this.getWorkspaceRoot(sourceRoot), "settings");
|
|
@@ -8242,8 +8855,8 @@ var init_client_unsafe_strategy2 = __esm({
|
|
|
8242
8855
|
getWorkspaceRoot(sourceRoot) {
|
|
8243
8856
|
return path25.resolve(sourceRoot ?? process.cwd(), ".canopy-dev");
|
|
8244
8857
|
}
|
|
8245
|
-
getContentRoot(sourceRoot) {
|
|
8246
|
-
return path25.resolve(sourceRoot ?? process.cwd(),
|
|
8858
|
+
getContentRoot(contentRoot, sourceRoot) {
|
|
8859
|
+
return path25.resolve(sourceRoot ?? process.cwd(), contentRoot);
|
|
8247
8860
|
}
|
|
8248
8861
|
getContentBranchesRoot(sourceRoot) {
|
|
8249
8862
|
return path25.join(this.getWorkspaceRoot(sourceRoot), "content-branches");
|
|
@@ -8273,8 +8886,7 @@ var init_client_unsafe_strategy2 = __esm({
|
|
|
8273
8886
|
getSettingsBranchName(config) {
|
|
8274
8887
|
if (config.settingsBranch)
|
|
8275
8888
|
return config.settingsBranch;
|
|
8276
|
-
|
|
8277
|
-
return `canopycms-settings-${deploymentName}`;
|
|
8889
|
+
return `canopycms-settings-${resolveDeploymentName3(config, "local")}`;
|
|
8278
8890
|
}
|
|
8279
8891
|
getSettingsRoot(sourceRoot) {
|
|
8280
8892
|
return path25.join(this.getWorkspaceRoot(sourceRoot), "settings");
|
|
@@ -8298,6 +8910,7 @@ var init_operating_mode2 = __esm({
|
|
|
8298
8910
|
"use strict";
|
|
8299
8911
|
init_client_safe_strategy2();
|
|
8300
8912
|
init_client_unsafe_strategy2();
|
|
8913
|
+
init_deployment_name2();
|
|
8301
8914
|
}
|
|
8302
8915
|
});
|
|
8303
8916
|
|
|
@@ -8307,12 +8920,12 @@ __export(sync_exports, {
|
|
|
8307
8920
|
SyncError: () => SyncError,
|
|
8308
8921
|
sync: () => sync
|
|
8309
8922
|
});
|
|
8310
|
-
import
|
|
8923
|
+
import fs24 from "node:fs/promises";
|
|
8311
8924
|
import path26 from "node:path";
|
|
8312
|
-
import { simpleGit as
|
|
8925
|
+
import { simpleGit as simpleGit7 } from "simple-git";
|
|
8313
8926
|
import * as p2 from "@clack/prompts";
|
|
8314
8927
|
async function detectCurrentBranch(projectDir) {
|
|
8315
|
-
const git =
|
|
8928
|
+
const git = simpleGit7({ baseDir: projectDir });
|
|
8316
8929
|
const branch = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
|
|
8317
8930
|
if (branch === "HEAD")
|
|
8318
8931
|
throw new Error("Detached HEAD \u2014 use --branch to specify a branch name");
|
|
@@ -8321,7 +8934,7 @@ async function detectCurrentBranch(projectDir) {
|
|
|
8321
8934
|
async function selectBranch(options, branchesDir) {
|
|
8322
8935
|
let branches = [];
|
|
8323
8936
|
if (await filePathExists(branchesDir)) {
|
|
8324
|
-
const entries = await
|
|
8937
|
+
const entries = await fs24.readdir(branchesDir, { withFileTypes: true });
|
|
8325
8938
|
branches = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
8326
8939
|
}
|
|
8327
8940
|
let branchName = options.branch;
|
|
@@ -8353,8 +8966,8 @@ async function selectBranch(options, branchesDir) {
|
|
|
8353
8966
|
assertWithinDir(branchPath, branchesDir, "--branch");
|
|
8354
8967
|
if (!branches.includes(branchName)) {
|
|
8355
8968
|
if (options.autoCreate) {
|
|
8356
|
-
await
|
|
8357
|
-
const wsGit =
|
|
8969
|
+
await fs24.mkdir(branchPath, { recursive: true });
|
|
8970
|
+
const wsGit = simpleGit7({ baseDir: branchPath });
|
|
8358
8971
|
await wsGit.init();
|
|
8359
8972
|
await wsGit.checkoutLocalBranch(branchName);
|
|
8360
8973
|
await wsGit.raw(["commit", "--allow-empty", "-m", "init: workspace created by sync"]);
|
|
@@ -8381,7 +8994,7 @@ async function syncPush(options) {
|
|
|
8381
8994
|
return { fileCount: 0 };
|
|
8382
8995
|
const branchPath = path26.join(branchesDir, branchName);
|
|
8383
8996
|
assertWithinDir(branchPath, branchesDir, "--branch");
|
|
8384
|
-
const wsGit =
|
|
8997
|
+
const wsGit = simpleGit7({ baseDir: branchPath });
|
|
8385
8998
|
const status = await wsGit.status();
|
|
8386
8999
|
if (status.conflicted.length > 0) {
|
|
8387
9000
|
p2.log.error("Branch workspace has unresolved merge conflicts.");
|
|
@@ -8451,7 +9064,7 @@ async function syncPull(options) {
|
|
|
8451
9064
|
p2.log.step(`Pulling content from branch: ${branchName}`);
|
|
8452
9065
|
const destContentDir = path26.join(projectDir, contentRoot);
|
|
8453
9066
|
assertWithinDir(destContentDir, projectDir, "--content-root");
|
|
8454
|
-
const sourceGit =
|
|
9067
|
+
const sourceGit = simpleGit7({ baseDir: projectDir });
|
|
8455
9068
|
const status = await sourceGit.status();
|
|
8456
9069
|
const uncommittedContent = status.files.filter((f) => f.path.startsWith(contentRoot + "/") || f.path === contentRoot);
|
|
8457
9070
|
const untrackedLosses = [];
|
|
@@ -8501,7 +9114,7 @@ async function syncPull(options) {
|
|
|
8501
9114
|
await copyDir(branchContentDir, tmpDestDir);
|
|
8502
9115
|
await safeReplaceDir(destContentDir, tmpDestDir);
|
|
8503
9116
|
} catch (err) {
|
|
8504
|
-
await
|
|
9117
|
+
await fs24.rm(tmpDestDir, { recursive: true, force: true }).catch(() => {
|
|
8505
9118
|
});
|
|
8506
9119
|
throw err;
|
|
8507
9120
|
}
|
|
@@ -8536,7 +9149,7 @@ async function syncBoth(options) {
|
|
|
8536
9149
|
return { pushed: 0, pulled: 0 };
|
|
8537
9150
|
const branchPath = path26.join(branchesDir, branchName);
|
|
8538
9151
|
assertWithinDir(branchPath, branchesDir, "--branch");
|
|
8539
|
-
const wsGit =
|
|
9152
|
+
const wsGit = simpleGit7({ baseDir: branchPath });
|
|
8540
9153
|
const status = await wsGit.status();
|
|
8541
9154
|
if (status.conflicted.length > 0) {
|
|
8542
9155
|
p2.log.error("Branch workspace has unresolved merge conflicts.");
|
|
@@ -8576,7 +9189,7 @@ async function syncBoth(options) {
|
|
|
8576
9189
|
await copyDir(srcContentDir, tmpDir);
|
|
8577
9190
|
await safeReplaceDir(wsContentDir, tmpDir);
|
|
8578
9191
|
} catch (err) {
|
|
8579
|
-
await
|
|
9192
|
+
await fs24.rm(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
8580
9193
|
});
|
|
8581
9194
|
await wsGit.checkout(currentBranch);
|
|
8582
9195
|
await wsGit.raw(["branch", "-D", incomingBranch]).catch(() => {
|
|
@@ -8639,7 +9252,7 @@ async function syncAbort(options) {
|
|
|
8639
9252
|
return;
|
|
8640
9253
|
const branchPath = path26.join(branchesDir, branchName);
|
|
8641
9254
|
assertWithinDir(branchPath, branchesDir, "--branch");
|
|
8642
|
-
const wsGit =
|
|
9255
|
+
const wsGit = simpleGit7({ baseDir: branchPath });
|
|
8643
9256
|
const status = await wsGit.status();
|
|
8644
9257
|
if (status.conflicted.length === 0) {
|
|
8645
9258
|
p2.log.info(`Branch workspace "${branchName}" is not in a merge state \u2014 nothing to abort.`);
|
|
@@ -8765,7 +9378,7 @@ var init_async_mutex = __esm({
|
|
|
8765
9378
|
});
|
|
8766
9379
|
|
|
8767
9380
|
// dist/utils/occ-json-write.js
|
|
8768
|
-
import
|
|
9381
|
+
import fs25 from "node:fs/promises";
|
|
8769
9382
|
import path27 from "node:path";
|
|
8770
9383
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
8771
9384
|
import lockfile4 from "proper-lockfile";
|
|
@@ -8776,28 +9389,28 @@ async function writeOccJsonFile2(filePath, payload, options) {
|
|
|
8776
9389
|
const data = { ...payload, version: newVersion, writeId };
|
|
8777
9390
|
const content = JSON.stringify(data, null, 2) + (trailingNewline ? "\n" : "");
|
|
8778
9391
|
const dir = path27.dirname(filePath);
|
|
8779
|
-
await
|
|
9392
|
+
await fs25.mkdir(dir, { recursive: true });
|
|
8780
9393
|
const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
8781
|
-
await
|
|
9394
|
+
await fs25.writeFile(tempPath, content, "utf-8");
|
|
8782
9395
|
if (expectedVersion === null) {
|
|
8783
9396
|
try {
|
|
8784
|
-
await
|
|
9397
|
+
await fs25.link(tempPath, filePath);
|
|
8785
9398
|
} catch (err) {
|
|
8786
|
-
await
|
|
9399
|
+
await fs25.unlink(tempPath).catch(() => {
|
|
8787
9400
|
});
|
|
8788
9401
|
if (isFileExistsError(err)) {
|
|
8789
9402
|
throw new OccWriteConflictError2();
|
|
8790
9403
|
}
|
|
8791
9404
|
throw err;
|
|
8792
9405
|
}
|
|
8793
|
-
await
|
|
9406
|
+
await fs25.unlink(tempPath).catch(() => {
|
|
8794
9407
|
});
|
|
8795
9408
|
return { version: newVersion, writeId };
|
|
8796
9409
|
}
|
|
8797
9410
|
try {
|
|
8798
9411
|
let currentVersion = null;
|
|
8799
9412
|
try {
|
|
8800
|
-
const current = JSON.parse(await
|
|
9413
|
+
const current = JSON.parse(await fs25.readFile(filePath, "utf-8"));
|
|
8801
9414
|
currentVersion = current.version ?? 0;
|
|
8802
9415
|
} catch {
|
|
8803
9416
|
currentVersion = null;
|
|
@@ -8805,16 +9418,16 @@ async function writeOccJsonFile2(filePath, payload, options) {
|
|
|
8805
9418
|
if (currentVersion !== expectedVersion) {
|
|
8806
9419
|
throw new OccWriteConflictError2();
|
|
8807
9420
|
}
|
|
8808
|
-
await
|
|
9421
|
+
await fs25.rename(tempPath, filePath);
|
|
8809
9422
|
if (settleMs > 0) {
|
|
8810
9423
|
await new Promise((resolve2) => setTimeout(resolve2, settleMs));
|
|
8811
9424
|
}
|
|
8812
|
-
const afterWrite = JSON.parse(await
|
|
9425
|
+
const afterWrite = JSON.parse(await fs25.readFile(filePath, "utf-8"));
|
|
8813
9426
|
if (afterWrite.writeId !== writeId) {
|
|
8814
9427
|
throw new OccWriteConflictError2();
|
|
8815
9428
|
}
|
|
8816
9429
|
} catch (err) {
|
|
8817
|
-
await
|
|
9430
|
+
await fs25.unlink(tempPath).catch(() => {
|
|
8818
9431
|
});
|
|
8819
9432
|
throw err;
|
|
8820
9433
|
}
|
|
@@ -8841,7 +9454,7 @@ async function withOccRetry2(operation, options) {
|
|
|
8841
9454
|
}
|
|
8842
9455
|
async function withOccFileLock2(filePath, fn) {
|
|
8843
9456
|
const dir = path27.dirname(filePath);
|
|
8844
|
-
await
|
|
9457
|
+
await fs25.mkdir(dir, { recursive: true });
|
|
8845
9458
|
const MAX_ATTEMPTS = 14;
|
|
8846
9459
|
let release = null;
|
|
8847
9460
|
for (let attempt = 1; release === null; attempt++) {
|
|
@@ -8900,7 +9513,7 @@ var init_occ_json_write = __esm({
|
|
|
8900
9513
|
});
|
|
8901
9514
|
|
|
8902
9515
|
// dist/branch-registry.js
|
|
8903
|
-
import
|
|
9516
|
+
import fs26 from "node:fs/promises";
|
|
8904
9517
|
import path28 from "node:path";
|
|
8905
9518
|
var log12, REGISTRY_FILE2, REGISTRY_STALE_FILE2, REGISTRY_TEMP_FILE2, REGISTRY_VERSION2, RESOURCE4, GET_MISS_REFRESH_MIN_INTERVAL_MS2, BranchRegistry2;
|
|
8906
9519
|
var init_branch_registry = __esm({
|
|
@@ -8909,6 +9522,7 @@ var init_branch_registry = __esm({
|
|
|
8909
9522
|
init_branch_metadata();
|
|
8910
9523
|
init_error();
|
|
8911
9524
|
init_debug2();
|
|
9525
|
+
init_logger2();
|
|
8912
9526
|
init_resource_generation();
|
|
8913
9527
|
log12 = createDebugLogger3({ prefix: "BranchRegistry" });
|
|
8914
9528
|
REGISTRY_FILE2 = "branches.json";
|
|
@@ -8933,7 +9547,7 @@ var init_branch_registry = __esm({
|
|
|
8933
9547
|
async list() {
|
|
8934
9548
|
let parsed;
|
|
8935
9549
|
try {
|
|
8936
|
-
const raw = await
|
|
9550
|
+
const raw = await fs26.readFile(this.registryPath, "utf8");
|
|
8937
9551
|
parsed = JSON.parse(raw);
|
|
8938
9552
|
} catch (err) {
|
|
8939
9553
|
if (isNotFoundError(err)) {
|
|
@@ -9022,7 +9636,7 @@ var init_branch_registry = __esm({
|
|
|
9022
9636
|
async performRegenerate() {
|
|
9023
9637
|
const read = await readResourceGeneration2(this.root, RESOURCE4);
|
|
9024
9638
|
const branches = await this.scanBranchDirectories();
|
|
9025
|
-
await
|
|
9639
|
+
await fs26.unlink(this.stalePath).catch(() => {
|
|
9026
9640
|
});
|
|
9027
9641
|
if (!read.ok) {
|
|
9028
9642
|
return branches;
|
|
@@ -9033,12 +9647,12 @@ var init_branch_registry = __esm({
|
|
|
9033
9647
|
generation: read.token
|
|
9034
9648
|
};
|
|
9035
9649
|
const uniqueTempPath = `${this.tempPath}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
9036
|
-
await
|
|
9037
|
-
await
|
|
9650
|
+
await fs26.mkdir(this.root, { recursive: true });
|
|
9651
|
+
await fs26.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
|
|
9038
9652
|
try {
|
|
9039
|
-
await
|
|
9653
|
+
await fs26.rename(uniqueTempPath, this.registryPath);
|
|
9040
9654
|
} catch (err) {
|
|
9041
|
-
await
|
|
9655
|
+
await fs26.unlink(uniqueTempPath).catch(() => {
|
|
9042
9656
|
});
|
|
9043
9657
|
throw err;
|
|
9044
9658
|
}
|
|
@@ -9052,7 +9666,7 @@ var init_branch_registry = __esm({
|
|
|
9052
9666
|
async scanBranchDirectories() {
|
|
9053
9667
|
const branches = [];
|
|
9054
9668
|
try {
|
|
9055
|
-
const entries = await
|
|
9669
|
+
const entries = await fs26.readdir(this.root, { withFileTypes: true });
|
|
9056
9670
|
for (const entry of entries) {
|
|
9057
9671
|
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
9058
9672
|
continue;
|
|
@@ -9062,7 +9676,7 @@ var init_branch_registry = __esm({
|
|
|
9062
9676
|
try {
|
|
9063
9677
|
meta = await BranchMetadataFileManager2.loadOnly(branchRoot);
|
|
9064
9678
|
} catch (err) {
|
|
9065
|
-
|
|
9679
|
+
canopyLogWarn3(`CanopyCMS: Skipping branch directory '${entry.name}' during registry scan: ${getErrorMessage(err)}`);
|
|
9066
9680
|
continue;
|
|
9067
9681
|
}
|
|
9068
9682
|
if (meta) {
|
|
@@ -9086,7 +9700,7 @@ var init_branch_registry = __esm({
|
|
|
9086
9700
|
});
|
|
9087
9701
|
|
|
9088
9702
|
// dist/branch-metadata.js
|
|
9089
|
-
import
|
|
9703
|
+
import fs27 from "node:fs/promises";
|
|
9090
9704
|
import path29 from "node:path";
|
|
9091
9705
|
var BRANCH_META_DIR2, BRANCH_META_FILE2, CURRENT_SCHEMA_VERSION2, BranchMetadataConflictError2, BranchMetadataCorruptError2, BranchMetadataFileManager2;
|
|
9092
9706
|
var init_branch_metadata = __esm({
|
|
@@ -9129,7 +9743,7 @@ var init_branch_metadata = __esm({
|
|
|
9129
9743
|
const resolvedRoot = path29.resolve(branchRoot);
|
|
9130
9744
|
const filePath = path29.join(resolvedRoot, BRANCH_META_DIR2, BRANCH_META_FILE2);
|
|
9131
9745
|
try {
|
|
9132
|
-
const raw = await
|
|
9746
|
+
const raw = await fs27.readFile(filePath, "utf8");
|
|
9133
9747
|
return JSON.parse(raw);
|
|
9134
9748
|
} catch (err) {
|
|
9135
9749
|
if (isNotFoundError(err)) {
|
|
@@ -9150,7 +9764,7 @@ var init_branch_metadata = __esm({
|
|
|
9150
9764
|
}
|
|
9151
9765
|
async load() {
|
|
9152
9766
|
try {
|
|
9153
|
-
const raw = await
|
|
9767
|
+
const raw = await fs27.readFile(this.filePath, "utf8");
|
|
9154
9768
|
const parsed = JSON.parse(raw);
|
|
9155
9769
|
const version = parsed.version ?? 0;
|
|
9156
9770
|
return { meta: parsed, version };
|
|
@@ -9212,7 +9826,7 @@ var init_branch_metadata = __esm({
|
|
|
9212
9826
|
*/
|
|
9213
9827
|
async save(incoming) {
|
|
9214
9828
|
try {
|
|
9215
|
-
await
|
|
9829
|
+
await fs27.stat(this.branchRoot);
|
|
9216
9830
|
} catch (err) {
|
|
9217
9831
|
if (isNotFoundError(err)) {
|
|
9218
9832
|
throw new BranchMetadataConflictError2("Branch no longer exists");
|
|
@@ -9287,7 +9901,7 @@ __export(migrate_exports, {
|
|
|
9287
9901
|
migrate: () => migrate,
|
|
9288
9902
|
slugifyName: () => slugifyName
|
|
9289
9903
|
});
|
|
9290
|
-
import
|
|
9904
|
+
import fs28 from "node:fs/promises";
|
|
9291
9905
|
import path30 from "node:path";
|
|
9292
9906
|
import * as p3 from "@clack/prompts";
|
|
9293
9907
|
function slugifyName(name) {
|
|
@@ -9313,7 +9927,7 @@ function stripDirIdSuffix(dirName) {
|
|
|
9313
9927
|
return dirName;
|
|
9314
9928
|
}
|
|
9315
9929
|
async function planDirectory(dirPath, isRoot, ctx) {
|
|
9316
|
-
const entries = await
|
|
9930
|
+
const entries = await fs28.readdir(dirPath, { withFileTypes: true });
|
|
9317
9931
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
9318
9932
|
const ops = [];
|
|
9319
9933
|
let hasOwnFiles = false;
|
|
@@ -9440,9 +10054,9 @@ async function migrate(options) {
|
|
|
9440
10054
|
const applyOps = async () => {
|
|
9441
10055
|
for (const op of ops) {
|
|
9442
10056
|
if (op.kind === "rename") {
|
|
9443
|
-
await
|
|
10057
|
+
await fs28.rename(op.from, op.to);
|
|
9444
10058
|
} else {
|
|
9445
|
-
await
|
|
10059
|
+
await fs28.writeFile(op.filePath, op.content, "utf-8");
|
|
9446
10060
|
}
|
|
9447
10061
|
}
|
|
9448
10062
|
};
|