canopycms 0.12.1 → 0.13.0
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/CHANGELOG.md +211 -49
- package/dist/api/branch-review.d.ts +20 -2
- package/dist/api/branch-review.js +112 -23
- package/dist/api/branch-status.d.ts +11 -2
- package/dist/api/branch-status.js +3 -2
- package/dist/api/branch-update-from-base.js +57 -6
- package/dist/api/branch.d.ts +5 -0
- package/dist/api/branch.js +27 -2
- package/dist/api/client.d.ts +2 -2
- package/dist/api/client.js +2 -2
- package/dist/api/content.js +1 -1
- package/dist/api/merge-conflicts.d.ts +10 -1
- package/dist/api/merge-conflicts.js +42 -7
- package/dist/api/types.d.ts +3 -2
- package/dist/base-update.d.ts +6 -0
- package/dist/branch-metadata-file.d.ts +15 -0
- package/dist/branch-metadata-file.js +5 -1
- package/dist/cli/cli.js +238 -108
- package/dist/cli/generate-ai-content.js +20 -6
- package/dist/cli/github-app-manifest.d.ts +4 -1
- package/dist/cli/github-app-manifest.js +5 -1
- package/dist/cli/init.js +7 -1
- package/dist/editor/Editor.js +39 -16
- package/dist/editor/admin/ConflictsStatusAlert.d.ts +8 -0
- package/dist/editor/admin/ConflictsStatusAlert.js +22 -0
- package/dist/editor/admin/SystemHealthPanel.js +4 -3
- package/dist/editor/base-update-wait.d.ts +4 -0
- package/dist/editor/base-update-wait.js +7 -0
- package/dist/editor/components/ConflictsGroup.d.ts +13 -1
- package/dist/editor/components/ConflictsGroup.js +52 -12
- package/dist/editor/components/EditorHeader.d.ts +3 -1
- package/dist/editor/components/EditorHeader.js +11 -7
- package/dist/editor/components/EntryChangesDrawer.d.ts +1 -1
- package/dist/editor/components/EntryChangesDrawer.js +58 -6
- package/dist/editor/conflict-cards.d.ts +4 -1
- package/dist/editor/conflict-cards.js +14 -9
- package/dist/editor/hooks/useBaseUpdate.d.ts +2 -0
- package/dist/editor/hooks/useBaseUpdate.js +12 -2
- package/dist/editor/hooks/useBranchChangesData.d.ts +7 -1
- package/dist/editor/hooks/useBranchChangesData.js +13 -0
- package/dist/editor/hooks/useBranchManager.d.ts +2 -0
- package/dist/editor/hooks/useBranchManager.js +80 -6
- package/dist/editor/hooks/useDraftManager.d.ts +8 -1
- package/dist/editor/hooks/useDraftManager.js +16 -1
- package/dist/editor/hooks/useEntryManager.d.ts +8 -3
- package/dist/editor/hooks/useEntryManager.js +6 -5
- package/dist/git-manager.d.ts +1 -0
- package/dist/git-manager.js +8 -3
- package/dist/merge/entry-merge.js +4 -3
- package/dist/merge/list-alignment.d.ts +9 -6
- package/dist/merge/list-alignment.js +65 -16
- package/dist/merge/rekey.d.ts +11 -0
- package/dist/merge/rekey.js +75 -0
- package/dist/operating-mode/client-safe-strategy.js +1 -1
- package/dist/operating-mode/client-unsafe-strategy.js +6 -0
- package/dist/operating-mode/types.d.ts +10 -2
- package/dist/services.d.ts +12 -4
- package/dist/services.js +56 -0
- package/dist/types.d.ts +35 -3
- package/dist/utils/git.d.ts +7 -0
- package/dist/utils/git.js +35 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker/branch-merge.d.ts +27 -3
- package/dist/worker/branch-merge.js +106 -5
- package/dist/worker/branch-policy.d.ts +18 -2
- package/dist/worker/branch-policy.js +36 -0
- package/dist/worker/cms-worker.d.ts +21 -0
- package/dist/worker/cms-worker.js +29 -4
- package/dist/worker/conflicts-status.d.ts +20 -0
- package/dist/worker/conflicts-status.js +204 -0
- package/dist/worker/field-merge.d.ts +17 -2
- package/dist/worker/field-merge.js +95 -2
- package/dist/worker/git-sync.d.ts +3 -2
- package/dist/worker/git-sync.js +9 -0
- package/dist/worker/github-commits.d.ts +3 -3
- package/dist/worker/github-commits.js +192 -40
- package/dist/worker/github-errors.d.ts +15 -0
- package/dist/worker/github-errors.js +47 -0
- package/dist/worker/github-gateway-http.js +7 -0
- package/dist/worker/github-gateway-wire.d.ts +30 -10
- package/dist/worker/github-gateway-wire.js +14 -2
- package/dist/worker/github-gateway.d.ts +17 -0
- package/dist/worker/github-gateway.js +37 -1
- package/dist/worker/history-rewrite.d.ts +40 -8
- package/dist/worker/history-rewrite.js +51 -23
- package/dist/worker/publish-decision.d.ts +37 -0
- package/dist/worker/publish-decision.js +16 -0
- package/dist/worker/rebase.d.ts +25 -5
- package/dist/worker/rebase.js +225 -81
- package/dist/worker/task-runner.js +16 -39
- package/dist/worker/worker-context.d.ts +6 -0
- package/package.json +1 -1
package/dist/cli/cli.js
CHANGED
|
@@ -1332,22 +1332,22 @@ CanopyCMS worker run-once (mode: ${mode}, auth: ${authMode})
|
|
|
1332
1332
|
process.exitCode = 1;
|
|
1333
1333
|
}
|
|
1334
1334
|
}
|
|
1335
|
-
const { dequeueTask:
|
|
1335
|
+
const { dequeueTask: dequeueTask22, completeTask: completeTask22, listTasks: listTasks22 } = await Promise.resolve().then(() => (init_cms_task_queue(), cms_task_queue_exports));
|
|
1336
1336
|
if (mode === "prod") {
|
|
1337
|
-
const pending = await
|
|
1337
|
+
const pending = await listTasks22(taskDir, "pending");
|
|
1338
1338
|
if (pending.length > 0) {
|
|
1339
1339
|
throw new Error(`workerRunOnce found ${pending.length} pending task(s) in prod mode but cannot execute them. Use the full worker daemon to process prod task queues.`);
|
|
1340
1340
|
}
|
|
1341
1341
|
console.log("No pending tasks");
|
|
1342
1342
|
} else {
|
|
1343
1343
|
let taskCount = 0;
|
|
1344
|
-
let task = await
|
|
1344
|
+
let task = await dequeueTask22(taskDir);
|
|
1345
1345
|
while (task !== null) {
|
|
1346
1346
|
console.log(`Processing task: ${task.action} (${task.id})`);
|
|
1347
1347
|
console.warn(` WARNING: Task skipped \u2014 GitHub operations require the full worker daemon`);
|
|
1348
|
-
await
|
|
1348
|
+
await completeTask22(taskDir, task.id, { skipped: true });
|
|
1349
1349
|
taskCount++;
|
|
1350
|
-
task = await
|
|
1350
|
+
task = await dequeueTask22(taskDir);
|
|
1351
1351
|
}
|
|
1352
1352
|
if (taskCount > 0) {
|
|
1353
1353
|
console.log(`Processed ${taskCount} task(s)`);
|
|
@@ -2144,7 +2144,7 @@ var init_init = __esm({
|
|
|
2144
2144
|
return false;
|
|
2145
2145
|
}
|
|
2146
2146
|
syncsBranchesWithBase() {
|
|
2147
|
-
return
|
|
2147
|
+
return true;
|
|
2148
2148
|
}
|
|
2149
2149
|
getPermissionsFileName() {
|
|
2150
2150
|
return "permissions.json";
|
|
@@ -2204,6 +2204,9 @@ var init_init = __esm({
|
|
|
2204
2204
|
autoDetectRemotePath: path8.join(this.getWorkspaceRoot(), "remote.git")
|
|
2205
2205
|
};
|
|
2206
2206
|
}
|
|
2207
|
+
baseUpdateRunsInRequest() {
|
|
2208
|
+
return false;
|
|
2209
|
+
}
|
|
2207
2210
|
requiresExistingRepo() {
|
|
2208
2211
|
return false;
|
|
2209
2212
|
}
|
|
@@ -2256,6 +2259,9 @@ var init_init = __esm({
|
|
|
2256
2259
|
envVarName: "CANOPYCMS_REMOTE_URL"
|
|
2257
2260
|
};
|
|
2258
2261
|
}
|
|
2262
|
+
baseUpdateRunsInRequest() {
|
|
2263
|
+
return true;
|
|
2264
|
+
}
|
|
2259
2265
|
requiresExistingRepo() {
|
|
2260
2266
|
return false;
|
|
2261
2267
|
}
|
|
@@ -2394,6 +2400,7 @@ var init_github_app_manifest = __esm({
|
|
|
2394
2400
|
CANOPY_APP_PERMISSIONS = {
|
|
2395
2401
|
contents: "write",
|
|
2396
2402
|
pull_requests: "write",
|
|
2403
|
+
statuses: "write",
|
|
2397
2404
|
metadata: "read"
|
|
2398
2405
|
};
|
|
2399
2406
|
APP_NAME_MAX_LENGTH = 34;
|
|
@@ -9230,7 +9237,7 @@ var init_generate_ai_content = __esm({
|
|
|
9230
9237
|
return false;
|
|
9231
9238
|
}
|
|
9232
9239
|
syncsBranchesWithBase() {
|
|
9233
|
-
return
|
|
9240
|
+
return true;
|
|
9234
9241
|
}
|
|
9235
9242
|
getPermissionsFileName() {
|
|
9236
9243
|
return "permissions.json";
|
|
@@ -9314,6 +9321,9 @@ var init_generate_ai_content = __esm({
|
|
|
9314
9321
|
autoDetectRemotePath: path92.join(this.getWorkspaceRoot(), "remote.git")
|
|
9315
9322
|
};
|
|
9316
9323
|
}
|
|
9324
|
+
baseUpdateRunsInRequest() {
|
|
9325
|
+
return false;
|
|
9326
|
+
}
|
|
9317
9327
|
requiresExistingRepo() {
|
|
9318
9328
|
return false;
|
|
9319
9329
|
}
|
|
@@ -9366,6 +9376,9 @@ var init_generate_ai_content = __esm({
|
|
|
9366
9376
|
envVarName: "CANOPYCMS_REMOTE_URL"
|
|
9367
9377
|
};
|
|
9368
9378
|
}
|
|
9379
|
+
baseUpdateRunsInRequest() {
|
|
9380
|
+
return true;
|
|
9381
|
+
}
|
|
9369
9382
|
requiresExistingRepo() {
|
|
9370
9383
|
return false;
|
|
9371
9384
|
}
|
|
@@ -12629,6 +12642,7 @@ var init_generate_ai_content = __esm({
|
|
|
12629
12642
|
archivedAt: z72.string().optional(),
|
|
12630
12643
|
rebaseFailure: z72.object({ message: z72.string(), firstAt: z72.string(), lastAt: z72.string() }).optional(),
|
|
12631
12644
|
historyRewrittenFrom: z72.string().optional(),
|
|
12645
|
+
historyRewrittenTo: z72.string().optional(),
|
|
12632
12646
|
lastGitHubTip: z72.string().optional(),
|
|
12633
12647
|
keptOverGitHub: z72.array(z72.string()).optional(),
|
|
12634
12648
|
rewrittenOnGitHub: z72.string().optional(),
|
|
@@ -12643,13 +12657,16 @@ var init_generate_ai_content = __esm({
|
|
|
12643
12657
|
z72.object({ kind: z72.literal("conflicts") }),
|
|
12644
12658
|
z72.object({ kind: z72.literal("retargeted"), base: z72.string() })
|
|
12645
12659
|
]).optional().catch(void 0),
|
|
12646
|
-
// Dropped when malformed: it
|
|
12660
|
+
// Dropped when malformed, never failing a read: it decorates the PR body, and without `commit`
|
|
12661
|
+
// the worker judges a move against the clone's head before it (worker/github-commits.ts).
|
|
12647
12662
|
review: z72.object({
|
|
12648
12663
|
state: z72.enum(["approved", "changes-requested"]),
|
|
12649
12664
|
by: z72.object({ userId: z72.string(), name: z72.string().optional() }),
|
|
12650
|
-
at: z72.string()
|
|
12665
|
+
at: z72.string(),
|
|
12666
|
+
commit: z72.string().optional()
|
|
12651
12667
|
}).optional().catch(void 0),
|
|
12652
12668
|
baseUpdateRequestedAt: z72.string().optional().catch(void 0),
|
|
12669
|
+
latestChangesWaiting: z72.string().optional().catch(void 0),
|
|
12653
12670
|
// Dropped when malformed: it only ends the editor's wait, which then times out instead.
|
|
12654
12671
|
lastBaseUpdate: z72.object({
|
|
12655
12672
|
at: z72.string(),
|
|
@@ -14389,10 +14406,14 @@ var init_generate_ai_content = __esm({
|
|
|
14389
14406
|
const status = await this.status();
|
|
14390
14407
|
return status.files.map((f) => f.path);
|
|
14391
14408
|
}
|
|
14409
|
+
/** The remote's URL as configured; `git remote -v`'s listing would split a path with a space. */
|
|
14392
14410
|
async getRemoteUrl() {
|
|
14393
|
-
|
|
14394
|
-
|
|
14395
|
-
|
|
14411
|
+
try {
|
|
14412
|
+
const url = (await this.git.raw(["remote", "get-url", this.remote])).trim();
|
|
14413
|
+
return url === "" ? void 0 : url;
|
|
14414
|
+
} catch {
|
|
14415
|
+
return void 0;
|
|
14416
|
+
}
|
|
14396
14417
|
}
|
|
14397
14418
|
/**
|
|
14398
14419
|
* Excludes `pattern` (e.g. `.canopy-meta/`) from this workspace's git, so it
|
|
@@ -17326,7 +17347,7 @@ var init_client_safe_strategy2 = __esm({
|
|
|
17326
17347
|
return false;
|
|
17327
17348
|
}
|
|
17328
17349
|
syncsBranchesWithBase() {
|
|
17329
|
-
return
|
|
17350
|
+
return true;
|
|
17330
17351
|
}
|
|
17331
17352
|
getPermissionsFileName() {
|
|
17332
17353
|
return "permissions.json";
|
|
@@ -17364,11 +17385,11 @@ var init_branch_name2 = __esm({
|
|
|
17364
17385
|
});
|
|
17365
17386
|
|
|
17366
17387
|
// dist/paths/normalize.js
|
|
17367
|
-
function normalizeFilesystemPath2(
|
|
17368
|
-
return
|
|
17388
|
+
function normalizeFilesystemPath2(path63) {
|
|
17389
|
+
return path63.split(/[\\/]+/).filter(Boolean).join("/");
|
|
17369
17390
|
}
|
|
17370
|
-
function hasTraversalSequence2(
|
|
17371
|
-
const normalized = normalizeFilesystemPath2(
|
|
17391
|
+
function hasTraversalSequence2(path63) {
|
|
17392
|
+
const normalized = normalizeFilesystemPath2(path63);
|
|
17372
17393
|
return normalized.includes("..");
|
|
17373
17394
|
}
|
|
17374
17395
|
function createLogicalPath2(...segments) {
|
|
@@ -17381,14 +17402,14 @@ function createLogicalPath2(...segments) {
|
|
|
17381
17402
|
function entryLogicalPath2(collectionLogicalPath, slug) {
|
|
17382
17403
|
return collectionLogicalPath ? `${collectionLogicalPath}/${slug}` : slug;
|
|
17383
17404
|
}
|
|
17384
|
-
function trimSlashes2(
|
|
17405
|
+
function trimSlashes2(path63) {
|
|
17385
17406
|
let start = 0;
|
|
17386
|
-
let end =
|
|
17387
|
-
while (start < end &&
|
|
17407
|
+
let end = path63.length;
|
|
17408
|
+
while (start < end && path63[start] === "/")
|
|
17388
17409
|
start++;
|
|
17389
|
-
while (end > start &&
|
|
17410
|
+
while (end > start && path63[end - 1] === "/")
|
|
17390
17411
|
end--;
|
|
17391
|
-
return
|
|
17412
|
+
return path63.slice(start, end);
|
|
17392
17413
|
}
|
|
17393
17414
|
var init_normalize2 = __esm({
|
|
17394
17415
|
"dist/paths/normalize.js"() {
|
|
@@ -17400,15 +17421,15 @@ var init_normalize2 = __esm({
|
|
|
17400
17421
|
function hasEmbeddedContentId(segment) {
|
|
17401
17422
|
return PHYSICAL_SEGMENT_PATTERN3.test(segment);
|
|
17402
17423
|
}
|
|
17403
|
-
function looksLikePhysicalPath(
|
|
17404
|
-
const segments =
|
|
17424
|
+
function looksLikePhysicalPath(path63) {
|
|
17425
|
+
const segments = path63.split("/");
|
|
17405
17426
|
return segments.some(hasEmbeddedContentId);
|
|
17406
17427
|
}
|
|
17407
|
-
function parseLogicalPath(
|
|
17408
|
-
if (!
|
|
17428
|
+
function parseLogicalPath(path63) {
|
|
17429
|
+
if (!path63 || typeof path63 !== "string") {
|
|
17409
17430
|
return { ok: false, error: "Path is required" };
|
|
17410
17431
|
}
|
|
17411
|
-
const normalized =
|
|
17432
|
+
const normalized = path63.replace(/\\/g, "/");
|
|
17412
17433
|
if (hasTraversalSequence2(normalized)) {
|
|
17413
17434
|
return { ok: false, error: "Path contains traversal sequence" };
|
|
17414
17435
|
}
|
|
@@ -17945,10 +17966,10 @@ function referenceValueId2(value) {
|
|
|
17945
17966
|
return void 0;
|
|
17946
17967
|
}
|
|
17947
17968
|
function collectReferenceIds(fields, data) {
|
|
17948
|
-
return traverseFields2(fields, data, ({ field, value, path:
|
|
17969
|
+
return traverseFields2(fields, data, ({ field, value, path: path63 }) => {
|
|
17949
17970
|
if (field.type !== "reference")
|
|
17950
17971
|
return [];
|
|
17951
|
-
const items = Array.isArray(value) ? value.map((item, index) => ({ item, path: `${
|
|
17972
|
+
const items = Array.isArray(value) ? value.map((item, index) => ({ item, path: `${path63}[${index}]` })) : [{ item: value, path: path63 }];
|
|
17952
17973
|
return items.flatMap(({ item, path: itemPath }) => {
|
|
17953
17974
|
const id = referenceValueId2(item);
|
|
17954
17975
|
return id ? [{ id, path: itemPath }] : [];
|
|
@@ -18146,6 +18167,9 @@ var init_client_unsafe_strategy2 = __esm({
|
|
|
18146
18167
|
autoDetectRemotePath: path31.join(this.getWorkspaceRoot(), "remote.git")
|
|
18147
18168
|
};
|
|
18148
18169
|
}
|
|
18170
|
+
baseUpdateRunsInRequest() {
|
|
18171
|
+
return false;
|
|
18172
|
+
}
|
|
18149
18173
|
requiresExistingRepo() {
|
|
18150
18174
|
return false;
|
|
18151
18175
|
}
|
|
@@ -18198,6 +18222,9 @@ var init_client_unsafe_strategy2 = __esm({
|
|
|
18198
18222
|
envVarName: "CANOPYCMS_REMOTE_URL"
|
|
18199
18223
|
};
|
|
18200
18224
|
}
|
|
18225
|
+
baseUpdateRunsInRequest() {
|
|
18226
|
+
return true;
|
|
18227
|
+
}
|
|
18201
18228
|
requiresExistingRepo() {
|
|
18202
18229
|
return false;
|
|
18203
18230
|
}
|
|
@@ -18586,6 +18613,7 @@ var init_branch_metadata_file = __esm({
|
|
|
18586
18613
|
archivedAt: z16.string().optional(),
|
|
18587
18614
|
rebaseFailure: z16.object({ message: z16.string(), firstAt: z16.string(), lastAt: z16.string() }).optional(),
|
|
18588
18615
|
historyRewrittenFrom: z16.string().optional(),
|
|
18616
|
+
historyRewrittenTo: z16.string().optional(),
|
|
18589
18617
|
lastGitHubTip: z16.string().optional(),
|
|
18590
18618
|
keptOverGitHub: z16.array(z16.string()).optional(),
|
|
18591
18619
|
rewrittenOnGitHub: z16.string().optional(),
|
|
@@ -18600,13 +18628,16 @@ var init_branch_metadata_file = __esm({
|
|
|
18600
18628
|
z16.object({ kind: z16.literal("conflicts") }),
|
|
18601
18629
|
z16.object({ kind: z16.literal("retargeted"), base: z16.string() })
|
|
18602
18630
|
]).optional().catch(void 0),
|
|
18603
|
-
// Dropped when malformed: it
|
|
18631
|
+
// Dropped when malformed, never failing a read: it decorates the PR body, and without `commit`
|
|
18632
|
+
// the worker judges a move against the clone's head before it (worker/github-commits.ts).
|
|
18604
18633
|
review: z16.object({
|
|
18605
18634
|
state: z16.enum(["approved", "changes-requested"]),
|
|
18606
18635
|
by: z16.object({ userId: z16.string(), name: z16.string().optional() }),
|
|
18607
|
-
at: z16.string()
|
|
18636
|
+
at: z16.string(),
|
|
18637
|
+
commit: z16.string().optional()
|
|
18608
18638
|
}).optional().catch(void 0),
|
|
18609
18639
|
baseUpdateRequestedAt: z16.string().optional().catch(void 0),
|
|
18640
|
+
latestChangesWaiting: z16.string().optional().catch(void 0),
|
|
18610
18641
|
// Dropped when malformed: it only ends the editor's wait, which then times out instead.
|
|
18611
18642
|
lastBaseUpdate: z16.object({
|
|
18612
18643
|
at: z16.string(),
|
|
@@ -19824,7 +19855,7 @@ function extractIdFromFilename2(filename) {
|
|
|
19824
19855
|
}
|
|
19825
19856
|
async function resolveCollectionPath2(root, logicalPath) {
|
|
19826
19857
|
const fs47 = await import("node:fs/promises");
|
|
19827
|
-
const
|
|
19858
|
+
const path63 = await import("node:path");
|
|
19828
19859
|
const segments = logicalPath.split("/").filter(Boolean);
|
|
19829
19860
|
let currentPath = root;
|
|
19830
19861
|
for (const segment of segments) {
|
|
@@ -19837,7 +19868,7 @@ async function resolveCollectionPath2(root, logicalPath) {
|
|
|
19837
19868
|
return logicalName === segment.toLowerCase();
|
|
19838
19869
|
});
|
|
19839
19870
|
if (matchingDir) {
|
|
19840
|
-
currentPath =
|
|
19871
|
+
currentPath = path63.join(currentPath, matchingDir.name);
|
|
19841
19872
|
} else {
|
|
19842
19873
|
return null;
|
|
19843
19874
|
}
|
|
@@ -21226,8 +21257,8 @@ function computeEntryUrl2(collection, slug, contentRoot) {
|
|
|
21226
21257
|
if (slug && !isIndexSlug2(slug)) {
|
|
21227
21258
|
segments.push(slug);
|
|
21228
21259
|
}
|
|
21229
|
-
const
|
|
21230
|
-
return
|
|
21260
|
+
const path63 = segments.length > 0 ? `/${segments.join("/")}` : "/";
|
|
21261
|
+
return path63.toLowerCase();
|
|
21231
21262
|
}
|
|
21232
21263
|
var init_entry_url = __esm({
|
|
21233
21264
|
"dist/utils/entry-url.js"() {
|
|
@@ -21730,8 +21761,8 @@ function detectFormatting2(text3) {
|
|
|
21730
21761
|
return { insertSpaces: false, tabSize: 1, eol };
|
|
21731
21762
|
return { insertSpaces: true, tabSize: indent?.length ?? 2, eol };
|
|
21732
21763
|
}
|
|
21733
|
-
function insertMember2(text3,
|
|
21734
|
-
const at2 = [...
|
|
21764
|
+
function insertMember2(text3, path63, key, value, formattingOptions) {
|
|
21765
|
+
const at2 = [...path63, key];
|
|
21735
21766
|
const [edit] = modify2(text3, at2, value, {});
|
|
21736
21767
|
const closing = edit ? /^[ \t]*\r?\n([ \t]*)\}/.exec(text3.slice(edit.offset)) : null;
|
|
21737
21768
|
if (edit && closing && edit.length === 0 && edit.content.startsWith(",")) {
|
|
@@ -21745,35 +21776,35 @@ function insertMember2(text3, path60, key, value, formattingOptions) {
|
|
|
21745
21776
|
}
|
|
21746
21777
|
return applyEdits2(text3, modify2(text3, at2, value, { formattingOptions }));
|
|
21747
21778
|
}
|
|
21748
|
-
function removeMember2(text3,
|
|
21749
|
-
return applyEdits2(text3, modify2(text3,
|
|
21779
|
+
function removeMember2(text3, path63) {
|
|
21780
|
+
return applyEdits2(text3, modify2(text3, path63, void 0, {}).map((edit) => {
|
|
21750
21781
|
const removed = text3.slice(edit.offset, edit.offset + edit.length);
|
|
21751
21782
|
const lead = /^\s+/.exec(removed)?.[0].length ?? 0;
|
|
21752
21783
|
const atMember = /\S/.test(text3.charAt(edit.offset + edit.length));
|
|
21753
21784
|
return edit.content === "" && lead > 0 && atMember ? { ...edit, offset: edit.offset + lead, length: edit.length - lead } : edit;
|
|
21754
21785
|
}));
|
|
21755
21786
|
}
|
|
21756
|
-
function reconcile2(text3,
|
|
21787
|
+
function reconcile2(text3, path63, before, after, formattingOptions) {
|
|
21757
21788
|
if (isRecord2(before) && isRecord2(after)) {
|
|
21758
21789
|
if (Object.keys(after).length === 0 && Object.keys(before).length > 0) {
|
|
21759
|
-
return applyEdits2(text3, modify2(text3,
|
|
21790
|
+
return applyEdits2(text3, modify2(text3, path63, {}, { formattingOptions }));
|
|
21760
21791
|
}
|
|
21761
21792
|
let next = text3;
|
|
21762
21793
|
for (const key of Object.keys(before)) {
|
|
21763
21794
|
if (!Object.prototype.hasOwnProperty.call(after, key))
|
|
21764
|
-
next = removeMember2(next, [...
|
|
21795
|
+
next = removeMember2(next, [...path63, key]);
|
|
21765
21796
|
}
|
|
21766
21797
|
for (const key of Object.keys(after)) {
|
|
21767
|
-
next = Object.prototype.hasOwnProperty.call(before, key) ? reconcile2(next, [...
|
|
21798
|
+
next = Object.prototype.hasOwnProperty.call(before, key) ? reconcile2(next, [...path63, key], before[key], after[key], formattingOptions) : insertMember2(next, path63, key, after[key], formattingOptions);
|
|
21768
21799
|
}
|
|
21769
21800
|
return next;
|
|
21770
21801
|
}
|
|
21771
21802
|
if (Array.isArray(before) && Array.isArray(after) && before.length === after.length) {
|
|
21772
|
-
return after.reduce((next, item, index) => reconcile2(next, [...
|
|
21803
|
+
return after.reduce((next, item, index) => reconcile2(next, [...path63, index], before[index], item, formattingOptions), text3);
|
|
21773
21804
|
}
|
|
21774
21805
|
if (JSON.stringify(before) === JSON.stringify(after))
|
|
21775
21806
|
return text3;
|
|
21776
|
-
return applyEdits2(text3, modify2(text3,
|
|
21807
|
+
return applyEdits2(text3, modify2(text3, path63, after, { formattingOptions }));
|
|
21777
21808
|
}
|
|
21778
21809
|
function serializeJson2(data, existingRaw) {
|
|
21779
21810
|
if (existingRaw === void 0)
|
|
@@ -23944,11 +23975,11 @@ var init_content_store = __esm({
|
|
|
23944
23975
|
getSchemaItems() {
|
|
23945
23976
|
return this.schemaIndex.values();
|
|
23946
23977
|
}
|
|
23947
|
-
assertSchemaItem(
|
|
23948
|
-
const normalized = normalizeFilesystemPath2(
|
|
23978
|
+
assertSchemaItem(path63) {
|
|
23979
|
+
const normalized = normalizeFilesystemPath2(path63);
|
|
23949
23980
|
const item = this.schemaIndex.get(normalized);
|
|
23950
23981
|
if (!item) {
|
|
23951
|
-
throw new ContentStoreError2(`Unknown schema item: ${
|
|
23982
|
+
throw new ContentStoreError2(`Unknown schema item: ${path63}`, "NO_SCHEMA_ITEM");
|
|
23952
23983
|
}
|
|
23953
23984
|
return item;
|
|
23954
23985
|
}
|
|
@@ -25781,30 +25812,30 @@ function isValidImageCropValue2(value) {
|
|
|
25781
25812
|
const rounded = roundCropRect2({ x, y, w, h });
|
|
25782
25813
|
return isValidCropRect2(rounded.x, rounded.y, rounded.w, rounded.h);
|
|
25783
25814
|
}
|
|
25784
|
-
function validateImageValue2(field, value,
|
|
25815
|
+
function validateImageValue2(field, value, path63) {
|
|
25785
25816
|
if (!isPlainRecord6(value)) {
|
|
25786
|
-
return [{ fieldPath:
|
|
25817
|
+
return [{ fieldPath: path63, message: "Expected an image object with { src, alt }" }];
|
|
25787
25818
|
}
|
|
25788
25819
|
const errors = [];
|
|
25789
25820
|
if (typeof value.src !== "string" || value.src.trim() === "") {
|
|
25790
|
-
errors.push({ fieldPath: `${
|
|
25821
|
+
errors.push({ fieldPath: `${path63}.src`, message: "Image src is required" });
|
|
25791
25822
|
}
|
|
25792
25823
|
if (typeof value.alt !== "string") {
|
|
25793
|
-
errors.push({ fieldPath: `${
|
|
25824
|
+
errors.push({ fieldPath: `${path63}.alt`, message: "Image alt text is required" });
|
|
25794
25825
|
} else if (value.alt.trim() === "" && field.altOptional !== true) {
|
|
25795
|
-
errors.push({ fieldPath: `${
|
|
25826
|
+
errors.push({ fieldPath: `${path63}.alt`, message: "Image alt text is required" });
|
|
25796
25827
|
}
|
|
25797
25828
|
if (value.width !== void 0 && !isPositiveInt2(value.width)) {
|
|
25798
|
-
errors.push({ fieldPath: `${
|
|
25829
|
+
errors.push({ fieldPath: `${path63}.width`, message: "Image width must be a positive integer" });
|
|
25799
25830
|
}
|
|
25800
25831
|
if (value.height !== void 0 && !isPositiveInt2(value.height)) {
|
|
25801
25832
|
errors.push({
|
|
25802
|
-
fieldPath: `${
|
|
25833
|
+
fieldPath: `${path63}.height`,
|
|
25803
25834
|
message: "Image height must be a positive integer"
|
|
25804
25835
|
});
|
|
25805
25836
|
}
|
|
25806
25837
|
if (value.crop !== void 0 && !isValidImageCropValue2(value.crop)) {
|
|
25807
|
-
errors.push({ fieldPath: `${
|
|
25838
|
+
errors.push({ fieldPath: `${path63}.crop`, message: "Invalid image crop rect" });
|
|
25808
25839
|
}
|
|
25809
25840
|
return errors;
|
|
25810
25841
|
}
|
|
@@ -25813,49 +25844,49 @@ function mergeBodyIntoData(fields, data, body) {
|
|
|
25813
25844
|
return data;
|
|
25814
25845
|
return { ...data, [findBodyFieldName2(fields)]: body };
|
|
25815
25846
|
}
|
|
25816
|
-
function validateScalar2(field, value,
|
|
25847
|
+
function validateScalar2(field, value, path63) {
|
|
25817
25848
|
switch (field.type) {
|
|
25818
25849
|
case "number":
|
|
25819
25850
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
25820
|
-
return [{ fieldPath:
|
|
25851
|
+
return [{ fieldPath: path63, message: "Expected a number" }];
|
|
25821
25852
|
}
|
|
25822
25853
|
return [];
|
|
25823
25854
|
case "boolean":
|
|
25824
25855
|
if (typeof value !== "boolean") {
|
|
25825
|
-
return [{ fieldPath:
|
|
25856
|
+
return [{ fieldPath: path63, message: "Expected true or false" }];
|
|
25826
25857
|
}
|
|
25827
25858
|
return [];
|
|
25828
25859
|
case "date":
|
|
25829
25860
|
if (value !== "" && !isCalendarDay2(value)) {
|
|
25830
|
-
return [{ fieldPath:
|
|
25861
|
+
return [{ fieldPath: path63, message: "Expected a real date, written YYYY-MM-DD" }];
|
|
25831
25862
|
}
|
|
25832
25863
|
return [];
|
|
25833
25864
|
case "datetime":
|
|
25834
25865
|
if (typeof value !== "string" || value !== "" && Number.isNaN(Date.parse(value))) {
|
|
25835
|
-
return [{ fieldPath:
|
|
25866
|
+
return [{ fieldPath: path63, message: "Expected a valid date/time string" }];
|
|
25836
25867
|
}
|
|
25837
25868
|
return [];
|
|
25838
25869
|
case "select": {
|
|
25839
25870
|
if (typeof value !== "string") {
|
|
25840
|
-
return [{ fieldPath:
|
|
25871
|
+
return [{ fieldPath: path63, message: "Expected a selection" }];
|
|
25841
25872
|
}
|
|
25842
25873
|
const options = selectOptionValues2(field);
|
|
25843
25874
|
if (value !== "" && !options.includes(value)) {
|
|
25844
|
-
return [{ fieldPath:
|
|
25875
|
+
return [{ fieldPath: path63, message: `Must be one of: ${options.join(", ")}` }];
|
|
25845
25876
|
}
|
|
25846
25877
|
return [];
|
|
25847
25878
|
}
|
|
25848
25879
|
case "reference":
|
|
25849
25880
|
if (referenceValueId2(value) === void 0) {
|
|
25850
|
-
return [{ fieldPath:
|
|
25881
|
+
return [{ fieldPath: path63, message: "Expected a reference id" }];
|
|
25851
25882
|
}
|
|
25852
25883
|
return [];
|
|
25853
25884
|
case "image":
|
|
25854
|
-
return validateImageValue2(field, value,
|
|
25885
|
+
return validateImageValue2(field, value, path63);
|
|
25855
25886
|
default:
|
|
25856
25887
|
if (STRING_FIELD_TYPES2.has(field.type)) {
|
|
25857
25888
|
if (typeof value !== "string") {
|
|
25858
|
-
return [{ fieldPath:
|
|
25889
|
+
return [{ fieldPath: path63, message: "Expected text" }];
|
|
25859
25890
|
}
|
|
25860
25891
|
}
|
|
25861
25892
|
return [];
|
|
@@ -25878,12 +25909,12 @@ function validateEntryData2(fields, data, pathPrefix = "", options = {}) {
|
|
|
25878
25909
|
errors.push(...validateEntryData2(field.fields, data, pathPrefix, options));
|
|
25879
25910
|
continue;
|
|
25880
25911
|
}
|
|
25881
|
-
const
|
|
25912
|
+
const path63 = pathPrefix ? `${pathPrefix}.${field.name}` : field.name;
|
|
25882
25913
|
const value = data[field.name];
|
|
25883
25914
|
const isRequired = options.required !== false && "required" in field && field.required === true;
|
|
25884
25915
|
if (isRequired && isEmptyForRequired2(field, value)) {
|
|
25885
25916
|
const message = Array.isArray(value) ? "At least one item is required" : "This field is required";
|
|
25886
|
-
errors.push({ fieldPath:
|
|
25917
|
+
errors.push({ fieldPath: path63, message });
|
|
25887
25918
|
continue;
|
|
25888
25919
|
}
|
|
25889
25920
|
if (value === void 0 || value === null)
|
|
@@ -25891,7 +25922,7 @@ function validateEntryData2(fields, data, pathPrefix = "", options = {}) {
|
|
|
25891
25922
|
if (Array.isArray(value) && "maxItems" in field && typeof field.maxItems === "number" && value.length > field.maxItems) {
|
|
25892
25923
|
const cap = field.maxItems;
|
|
25893
25924
|
errors.push({
|
|
25894
|
-
fieldPath:
|
|
25925
|
+
fieldPath: path63,
|
|
25895
25926
|
message: `At most ${cap} ${cap === 1 ? "item" : "items"}; this list has ${value.length}`,
|
|
25896
25927
|
maxItems: { count: value.length, cap }
|
|
25897
25928
|
});
|
|
@@ -25899,11 +25930,11 @@ function validateEntryData2(fields, data, pathPrefix = "", options = {}) {
|
|
|
25899
25930
|
if (field.type === "block") {
|
|
25900
25931
|
const blockField = field;
|
|
25901
25932
|
if (!Array.isArray(value)) {
|
|
25902
|
-
errors.push({ fieldPath:
|
|
25933
|
+
errors.push({ fieldPath: path63, message: "Expected a list of blocks" });
|
|
25903
25934
|
continue;
|
|
25904
25935
|
}
|
|
25905
25936
|
value.forEach((item, index) => {
|
|
25906
|
-
const itemPath = `${
|
|
25937
|
+
const itemPath = `${path63}[${index}]`;
|
|
25907
25938
|
if (!isPlainRecord6(item)) {
|
|
25908
25939
|
errors.push({ fieldPath: itemPath, message: "Expected a block object" });
|
|
25909
25940
|
return;
|
|
@@ -25925,11 +25956,11 @@ function validateEntryData2(fields, data, pathPrefix = "", options = {}) {
|
|
|
25925
25956
|
const objectField = field;
|
|
25926
25957
|
if (objectField.list) {
|
|
25927
25958
|
if (!Array.isArray(value)) {
|
|
25928
|
-
errors.push({ fieldPath:
|
|
25959
|
+
errors.push({ fieldPath: path63, message: "Expected a list of items" });
|
|
25929
25960
|
continue;
|
|
25930
25961
|
}
|
|
25931
25962
|
value.forEach((item, index) => {
|
|
25932
|
-
const itemPath = `${
|
|
25963
|
+
const itemPath = `${path63}[${index}]`;
|
|
25933
25964
|
if (!isPlainRecord6(item)) {
|
|
25934
25965
|
errors.push({ fieldPath: itemPath, message: "Expected an object" });
|
|
25935
25966
|
return;
|
|
@@ -25939,36 +25970,36 @@ function validateEntryData2(fields, data, pathPrefix = "", options = {}) {
|
|
|
25939
25970
|
}
|
|
25940
25971
|
});
|
|
25941
25972
|
} else if (!isPlainRecord6(value)) {
|
|
25942
|
-
errors.push({ fieldPath:
|
|
25973
|
+
errors.push({ fieldPath: path63, message: "Expected an object" });
|
|
25943
25974
|
} else if (objectField.fields) {
|
|
25944
|
-
errors.push(...validateEntryData2(objectField.fields, value,
|
|
25975
|
+
errors.push(...validateEntryData2(objectField.fields, value, path63, options));
|
|
25945
25976
|
}
|
|
25946
25977
|
continue;
|
|
25947
25978
|
}
|
|
25948
25979
|
if ("list" in field && field.list) {
|
|
25949
25980
|
if (!Array.isArray(value)) {
|
|
25950
|
-
errors.push({ fieldPath:
|
|
25981
|
+
errors.push({ fieldPath: path63, message: "Expected a list of values" });
|
|
25951
25982
|
continue;
|
|
25952
25983
|
}
|
|
25953
25984
|
value.forEach((item, index) => {
|
|
25954
|
-
errors.push(...validateScalar2(field, item, `${
|
|
25985
|
+
errors.push(...validateScalar2(field, item, `${path63}[${index}]`));
|
|
25955
25986
|
});
|
|
25956
25987
|
continue;
|
|
25957
25988
|
}
|
|
25958
25989
|
if (Array.isArray(value) && KNOWN_FIELD_TYPES2.has(field.type)) {
|
|
25959
|
-
errors.push({ fieldPath:
|
|
25990
|
+
errors.push({ fieldPath: path63, message: "Expected a single value, not a list" });
|
|
25960
25991
|
continue;
|
|
25961
25992
|
}
|
|
25962
|
-
errors.push(...validateScalar2(field, value,
|
|
25993
|
+
errors.push(...validateScalar2(field, value, path63));
|
|
25963
25994
|
}
|
|
25964
25995
|
return errors;
|
|
25965
25996
|
}
|
|
25966
25997
|
function findUnknownKeys(fields, data) {
|
|
25967
|
-
return traverseFields2(fields, data, () => [], "", ({ fields: containerFields, data: record, path:
|
|
25998
|
+
return traverseFields2(fields, data, () => [], "", ({ fields: containerFields, data: record, path: path63 }) => {
|
|
25968
25999
|
if (containerFields.length === 0)
|
|
25969
26000
|
return [];
|
|
25970
26001
|
const known = new Set(flattenGroupFields2(containerFields).map((field) => field.name));
|
|
25971
|
-
return Object.keys(record).filter((key) => !known.has(key) && !BLOCK_STRUCTURAL_KEYS2.has(key) && key !== ITEM_KEY2).map((key) =>
|
|
26002
|
+
return Object.keys(record).filter((key) => !known.has(key) && !BLOCK_STRUCTURAL_KEYS2.has(key) && key !== ITEM_KEY2).map((key) => path63 ? `${path63}.${key}` : key);
|
|
25972
26003
|
});
|
|
25973
26004
|
}
|
|
25974
26005
|
function normalizeReferenceValues(fields, data) {
|
|
@@ -26511,11 +26542,11 @@ function findMarkdownSafetyIssues(fields, format, data, siteAllow) {
|
|
|
26511
26542
|
if (policy !== void 0)
|
|
26512
26543
|
findings.push(...findInValue(bodyName, data[bodyName], policy));
|
|
26513
26544
|
}
|
|
26514
|
-
findings.push(...traverseFields2(fields, data, ({ field, value, path:
|
|
26515
|
-
if (
|
|
26545
|
+
findings.push(...traverseFields2(fields, data, ({ field, value, path: path63 }) => {
|
|
26546
|
+
if (path63 === bodyName || !isMarkdownField(field))
|
|
26516
26547
|
return [];
|
|
26517
26548
|
const policy = markdownPolicyOf(field, siteAllow);
|
|
26518
|
-
return policy === void 0 ? [] : findInValue(
|
|
26549
|
+
return policy === void 0 ? [] : findInValue(path63, value, policy);
|
|
26519
26550
|
}));
|
|
26520
26551
|
return findings;
|
|
26521
26552
|
}
|
|
@@ -26639,17 +26670,17 @@ var init_markdown_safety = __esm({
|
|
|
26639
26670
|
});
|
|
26640
26671
|
|
|
26641
26672
|
// dist/authorization/validation.js
|
|
26642
|
-
function parsePermissionPath(
|
|
26643
|
-
if (!
|
|
26673
|
+
function parsePermissionPath(path63) {
|
|
26674
|
+
if (!path63 || typeof path63 !== "string") {
|
|
26644
26675
|
return { ok: false, error: "Permission path is required" };
|
|
26645
26676
|
}
|
|
26646
|
-
if (hasTraversalSequence2(
|
|
26677
|
+
if (hasTraversalSequence2(path63)) {
|
|
26647
26678
|
return {
|
|
26648
26679
|
ok: false,
|
|
26649
26680
|
error: "Permission path contains traversal sequence (..)"
|
|
26650
26681
|
};
|
|
26651
26682
|
}
|
|
26652
|
-
const normalized =
|
|
26683
|
+
const normalized = path63.replace(/\\/g, "/");
|
|
26653
26684
|
if (normalized.startsWith("/") || normalized.endsWith("/")) {
|
|
26654
26685
|
return {
|
|
26655
26686
|
ok: false,
|
|
@@ -26749,6 +26780,79 @@ var init_validators = __esm({
|
|
|
26749
26780
|
}
|
|
26750
26781
|
});
|
|
26751
26782
|
|
|
26783
|
+
// dist/base-update.js
|
|
26784
|
+
var SYNC_BRANCH_ACTION, BASE_UPDATE_GIVE_UP_MS, BASE_UPDATE_EARLY_RETRY_MS;
|
|
26785
|
+
var init_base_update = __esm({
|
|
26786
|
+
"dist/base-update.js"() {
|
|
26787
|
+
"use strict";
|
|
26788
|
+
SYNC_BRANCH_ACTION = "sync-branch";
|
|
26789
|
+
BASE_UPDATE_GIVE_UP_MS = 10 * 6e4;
|
|
26790
|
+
BASE_UPDATE_EARLY_RETRY_MS = 2 * 6e4;
|
|
26791
|
+
}
|
|
26792
|
+
});
|
|
26793
|
+
|
|
26794
|
+
// dist/task-queue/task-queue.js
|
|
26795
|
+
import path57 from "node:path";
|
|
26796
|
+
import crypto3 from "node:crypto";
|
|
26797
|
+
async function enqueueTask2(taskDir, task, logger = nullLogger2) {
|
|
26798
|
+
const id = crypto3.randomUUID();
|
|
26799
|
+
const pendingDir = path57.join(taskDir, "pending");
|
|
26800
|
+
const queuedTask = {
|
|
26801
|
+
id,
|
|
26802
|
+
action: task.action,
|
|
26803
|
+
payload: task.payload,
|
|
26804
|
+
status: "pending",
|
|
26805
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26806
|
+
retryCount: 0,
|
|
26807
|
+
maxRetries: task.maxRetries ?? DEFAULT_MAX_RETRIES2
|
|
26808
|
+
};
|
|
26809
|
+
const filePath = path57.join(pendingDir, `${id}.json`);
|
|
26810
|
+
await atomicWriteFile3(filePath, JSON.stringify(queuedTask, null, 2));
|
|
26811
|
+
logger.debug("Enqueued task", { id, action: task.action });
|
|
26812
|
+
return id;
|
|
26813
|
+
}
|
|
26814
|
+
var DEFAULT_MAX_RETRIES2, nullLogger2;
|
|
26815
|
+
var init_task_queue2 = __esm({
|
|
26816
|
+
"dist/task-queue/task-queue.js"() {
|
|
26817
|
+
"use strict";
|
|
26818
|
+
init_atomic_write3();
|
|
26819
|
+
DEFAULT_MAX_RETRIES2 = 3;
|
|
26820
|
+
nullLogger2 = { debug: () => {
|
|
26821
|
+
} };
|
|
26822
|
+
}
|
|
26823
|
+
});
|
|
26824
|
+
|
|
26825
|
+
// dist/task-queue/cms-task-queue.js
|
|
26826
|
+
var debugLogger2;
|
|
26827
|
+
var init_cms_task_queue2 = __esm({
|
|
26828
|
+
"dist/task-queue/cms-task-queue.js"() {
|
|
26829
|
+
"use strict";
|
|
26830
|
+
init_debug2();
|
|
26831
|
+
init_task_queue2();
|
|
26832
|
+
debugLogger2 = createDebugLogger3({ prefix: "TaskQueue" });
|
|
26833
|
+
}
|
|
26834
|
+
});
|
|
26835
|
+
|
|
26836
|
+
// dist/task-queue/task-queue-config.js
|
|
26837
|
+
import path58 from "node:path";
|
|
26838
|
+
function getTaskQueueDir3(config) {
|
|
26839
|
+
switch (config.mode) {
|
|
26840
|
+
case "prod": {
|
|
26841
|
+
const workspace = process.env.CANOPYCMS_WORKSPACE_ROOT ?? DEFAULT_PROD_WORKSPACE3;
|
|
26842
|
+
return path58.join(path58.resolve(workspace), ".tasks");
|
|
26843
|
+
}
|
|
26844
|
+
case "dev": {
|
|
26845
|
+
return path58.join(process.cwd(), ".canopy-dev", ".tasks");
|
|
26846
|
+
}
|
|
26847
|
+
}
|
|
26848
|
+
}
|
|
26849
|
+
var init_task_queue_config3 = __esm({
|
|
26850
|
+
"dist/task-queue/task-queue-config.js"() {
|
|
26851
|
+
"use strict";
|
|
26852
|
+
init_config5();
|
|
26853
|
+
}
|
|
26854
|
+
});
|
|
26855
|
+
|
|
26752
26856
|
// dist/merge/conflict-path.js
|
|
26753
26857
|
function describePath(fields, read, at2) {
|
|
26754
26858
|
let key = "";
|
|
@@ -26860,7 +26964,7 @@ var init_conflict_path = __esm({
|
|
|
26860
26964
|
// dist/merge-conflicts.js
|
|
26861
26965
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
26862
26966
|
import fs45 from "node:fs/promises";
|
|
26863
|
-
import
|
|
26967
|
+
import path59 from "node:path";
|
|
26864
26968
|
import { z as z23 } from "zod";
|
|
26865
26969
|
function withoutResolvedFields(entries, contentId, resolved) {
|
|
26866
26970
|
const entry = entries[contentId];
|
|
@@ -26969,7 +27073,7 @@ var init_merge_conflicts = __esm({
|
|
|
26969
27073
|
MergeConflictStore = class {
|
|
26970
27074
|
constructor(branchRoot, settleMs) {
|
|
26971
27075
|
this.settleMs = settleMs;
|
|
26972
|
-
this.filePath =
|
|
27076
|
+
this.filePath = path59.join(path59.resolve(branchRoot), ".canopy-meta", MERGE_CONFLICTS_FILE);
|
|
26973
27077
|
}
|
|
26974
27078
|
/** Whether the clone has a record at all. */
|
|
26975
27079
|
async exists() {
|
|
@@ -26994,7 +27098,7 @@ var init_merge_conflicts = __esm({
|
|
|
26994
27098
|
const stamp = `${Date.now()}-${randomUUID7().slice(0, 8)}`;
|
|
26995
27099
|
const aside = `${this.filePath}.malformed-${stamp}`;
|
|
26996
27100
|
await fs45.copyFile(this.filePath, aside);
|
|
26997
|
-
const held = { aside:
|
|
27101
|
+
const held = { aside: path59.basename(aside), at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
26998
27102
|
const next = `${this.filePath}.held-${stamp}`;
|
|
26999
27103
|
await writeOccJsonFile2(next, { schema: RECORD_SCHEMA, entries: {}, held }, { expectedVersion: null, settleMs: this.settleMs });
|
|
27000
27104
|
await fs45.rename(next, this.filePath);
|
|
@@ -27062,11 +27166,20 @@ var init_merge_conflicts = __esm({
|
|
|
27062
27166
|
});
|
|
27063
27167
|
|
|
27064
27168
|
// dist/api/merge-conflicts.js
|
|
27065
|
-
import
|
|
27169
|
+
import path60 from "node:path";
|
|
27066
27170
|
import { z as z24 } from "zod";
|
|
27067
|
-
async function
|
|
27171
|
+
async function wakeConflictsStatus(config, branch) {
|
|
27172
|
+
if (operatingStrategy3(config.mode).baseUpdateRunsInRequest())
|
|
27173
|
+
return;
|
|
27174
|
+
try {
|
|
27175
|
+
await enqueueTask2(getTaskQueueDir3(config), { action: SYNC_BRANCH_ACTION, payload: { branch } });
|
|
27176
|
+
} catch (err2) {
|
|
27177
|
+
canopyLogWarn3(`CanopyCMS: Could not queue a sync for ${branch} after its last conflict was resolved:`, getErrorMessage(err2));
|
|
27178
|
+
}
|
|
27179
|
+
}
|
|
27180
|
+
async function clearFieldConflicts(config, branchContext, contentId, resolved) {
|
|
27068
27181
|
let cleared = [];
|
|
27069
|
-
await changeRecord(branchContext, (entries) => {
|
|
27182
|
+
await changeRecord(config, branchContext, (entries) => {
|
|
27070
27183
|
const next = withoutResolvedFields(entries, contentId, resolved);
|
|
27071
27184
|
cleared = next.cleared;
|
|
27072
27185
|
return next.cleared.length > 0 ? next.entries : null;
|
|
@@ -27077,6 +27190,10 @@ var MAX_SIDE_BYTES, MAX_VERSIONS_BYTES, FORMATS, RECORD_UNREADABLE_ERROR, NO_CON
|
|
|
27077
27190
|
var init_merge_conflicts2 = __esm({
|
|
27078
27191
|
"dist/api/merge-conflicts.js"() {
|
|
27079
27192
|
"use strict";
|
|
27193
|
+
init_base_update();
|
|
27194
|
+
init_operating_mode2();
|
|
27195
|
+
init_cms_task_queue2();
|
|
27196
|
+
init_task_queue_config3();
|
|
27080
27197
|
init_content_listing();
|
|
27081
27198
|
init_content_store();
|
|
27082
27199
|
init_conflict_path();
|
|
@@ -27127,7 +27244,7 @@ var init_merge_conflicts2 = __esm({
|
|
|
27127
27244
|
return ref.logicalPath;
|
|
27128
27245
|
if (ref.kind === "collection-meta")
|
|
27129
27246
|
return ref.collectionPath;
|
|
27130
|
-
const settings = refOf2(`${
|
|
27247
|
+
const settings = refOf2(`${path60.posix.dirname(at2)}/.collection.json`, flatSchema, contentRoot);
|
|
27131
27248
|
return settings.kind === "collection-meta" ? settings.collectionPath : void 0;
|
|
27132
27249
|
};
|
|
27133
27250
|
locateRecords = async (store, entries, flatSchema, contentRoot) => {
|
|
@@ -27161,7 +27278,7 @@ var init_merge_conflicts2 = __esm({
|
|
|
27161
27278
|
return admin;
|
|
27162
27279
|
return [record.address, record.recordedAddress ?? record.address].every((at2) => checkAccess(at2, level).allowed);
|
|
27163
27280
|
};
|
|
27164
|
-
changeRecord = async (branchContext, change) => {
|
|
27281
|
+
changeRecord = async (config, branchContext, change) => {
|
|
27165
27282
|
const state = await new MergeConflictStore(branchContext.branchRoot).update((current) => {
|
|
27166
27283
|
if (current === null)
|
|
27167
27284
|
return null;
|
|
@@ -27171,6 +27288,9 @@ var init_merge_conflicts2 = __esm({
|
|
|
27171
27288
|
if (state === null)
|
|
27172
27289
|
return null;
|
|
27173
27290
|
await getBranchMetadataFileManager(branchContext.branchRoot, branchContext.baseRoot).update((existing) => existing?.branch.name === branchContext.branch.name ? { branch: conflictFilesOf(state.entries) } : null, { keepUpdatedAt: true });
|
|
27291
|
+
if (openConflictCount(state.entries) === 0 && !state.held) {
|
|
27292
|
+
await wakeConflictsStatus(config, branchContext.branch.name);
|
|
27293
|
+
}
|
|
27174
27294
|
return state.entries;
|
|
27175
27295
|
};
|
|
27176
27296
|
entryTypeOf = (flatSchema, ref) => flatSchema.find((item) => item.type === "collection" && item.logicalPath === ref.collectionPath)?.entries?.find((e) => e.name === ref.entryType);
|
|
@@ -27398,7 +27518,9 @@ var init_merge_conflicts2 = __esm({
|
|
|
27398
27518
|
* latest version has it (`deleted-on-branch`); `use-latest`: replace an entry that can't be merged
|
|
27399
27519
|
* field by field with the latest version (`unreadable`).
|
|
27400
27520
|
*/
|
|
27401
|
-
choice: z24.enum(["keep", "delete", "restore", "use-latest"])
|
|
27521
|
+
choice: z24.enum(["keep", "delete", "restore", "use-latest"]),
|
|
27522
|
+
/** The card promised the entry stays deleted: a `keep` is refused if it is in the tree now. */
|
|
27523
|
+
entryGone: z24.literal(true).optional()
|
|
27402
27524
|
});
|
|
27403
27525
|
CHOICE_KINDS = {
|
|
27404
27526
|
keep: ["fields", "deleted-on-base", "deleted-on-branch", "unreadable"],
|
|
@@ -27407,18 +27529,18 @@ var init_merge_conflicts2 = __esm({
|
|
|
27407
27529
|
"use-latest": ["unreadable"]
|
|
27408
27530
|
};
|
|
27409
27531
|
sameKindAtLatest = async (git, entry, id, placed, flatSchema, contentRoot) => {
|
|
27410
|
-
const own = parseTypedFilename2(
|
|
27532
|
+
const own = parseTypedFilename2(path60.posix.basename(placed.physicalPath));
|
|
27411
27533
|
let files;
|
|
27412
27534
|
try {
|
|
27413
27535
|
files = await git.listFilesWithSizeAt(`${entry.mergeSha}^2`, contentRoot);
|
|
27414
27536
|
} catch {
|
|
27415
27537
|
return false;
|
|
27416
27538
|
}
|
|
27417
|
-
const latest = files.find((f) => parseTypedFilename2(
|
|
27539
|
+
const latest = files.find((f) => parseTypedFilename2(path60.posix.basename(f.path))?.id === id);
|
|
27418
27540
|
if (!latest || !own)
|
|
27419
27541
|
return false;
|
|
27420
27542
|
const latestRef = entryRefFromRepoPath(latest.path, flatSchema, contentRoot);
|
|
27421
|
-
return latestRef.kind === "entry" && latestRef.collectionPath === placed.collectionPath && latestRef.entryType === own.type &&
|
|
27543
|
+
return latestRef.kind === "entry" && latestRef.collectionPath === placed.collectionPath && latestRef.entryType === own.type && path60.posix.extname(latest.path) === path60.posix.extname(placed.physicalPath);
|
|
27422
27544
|
};
|
|
27423
27545
|
ConflictChangedError = class extends Error {
|
|
27424
27546
|
};
|
|
@@ -27466,9 +27588,14 @@ var init_merge_conflicts2 = __esm({
|
|
|
27466
27588
|
if (current?.kind !== entry.kind || current.mergeSha !== body.mergeSha) {
|
|
27467
27589
|
throw new ConflictChangedError();
|
|
27468
27590
|
}
|
|
27591
|
+
if (body.entryGone) {
|
|
27592
|
+
store.invalidateIndex();
|
|
27593
|
+
if ((await store.idIndex()).findById(body.id) !== null)
|
|
27594
|
+
throw new ConflictChangedError();
|
|
27595
|
+
}
|
|
27469
27596
|
};
|
|
27470
27597
|
const clear = async () => {
|
|
27471
|
-
const written = await changeRecord(branchContext, (now) => {
|
|
27598
|
+
const written = await changeRecord(ctx.services.config, branchContext, (now) => {
|
|
27472
27599
|
const current = now[body.id];
|
|
27473
27600
|
if (current?.kind === "fields" && current.mergeSha === body.mergeSha) {
|
|
27474
27601
|
const { [body.id]: _kept, ...rest } = now;
|
|
@@ -27586,7 +27713,7 @@ var init_merge_conflicts2 = __esm({
|
|
|
27586
27713
|
guards: ["schema", "writableBranch"],
|
|
27587
27714
|
handler: resolveHandler
|
|
27588
27715
|
});
|
|
27589
|
-
releaseHoldHandler = async (gc) => {
|
|
27716
|
+
releaseHoldHandler = async (gc, ctx) => {
|
|
27590
27717
|
const { branchRoot } = gc.branchContext;
|
|
27591
27718
|
try {
|
|
27592
27719
|
const written = await withContentWriteLock2(branchRoot, () => new MergeConflictStore(branchRoot).update((current) => {
|
|
@@ -27594,6 +27721,9 @@ var init_merge_conflicts2 = __esm({
|
|
|
27594
27721
|
return null;
|
|
27595
27722
|
return { ...current, held: void 0 };
|
|
27596
27723
|
}));
|
|
27724
|
+
if (written !== null && openConflictCount(written.entries) === 0) {
|
|
27725
|
+
await wakeConflictsStatus(ctx.services.config, gc.branchContext.branch.name);
|
|
27726
|
+
}
|
|
27597
27727
|
return { ok: true, status: 200, data: { released: written !== null } };
|
|
27598
27728
|
} catch (err2) {
|
|
27599
27729
|
if (err2 instanceof ContentWriteLockBusyError2) {
|
|
@@ -27976,7 +28106,7 @@ var init_content = __esm({
|
|
|
27976
28106
|
if (!id || resolving.length === 0)
|
|
27977
28107
|
return;
|
|
27978
28108
|
try {
|
|
27979
|
-
resolvedConflicts = await clearFieldConflicts(branchContext, id, resolving);
|
|
28109
|
+
resolvedConflicts = await clearFieldConflicts(ctx.services.config, branchContext, id, resolving);
|
|
27980
28110
|
} catch (err2) {
|
|
27981
28111
|
canopyLogWarn3(`CanopyCMS: saved ${logicalPathSegments.join("/")} but couldn't mark its conflicts resolved:`, getErrorMessage(err2));
|
|
27982
28112
|
resolvedConflicts = [];
|
|
@@ -28205,7 +28335,7 @@ __export(backfill_item_keys_exports, {
|
|
|
28205
28335
|
backfillItemKeysCLI: () => backfillItemKeysCLI
|
|
28206
28336
|
});
|
|
28207
28337
|
import fs46 from "node:fs/promises";
|
|
28208
|
-
import
|
|
28338
|
+
import path61 from "node:path";
|
|
28209
28339
|
import { diffArrays } from "diff";
|
|
28210
28340
|
import { simpleGit as simpleGit8 } from "simple-git";
|
|
28211
28341
|
async function backfillItemKeys(options) {
|
|
@@ -28217,7 +28347,7 @@ async function backfillItemKeys(options) {
|
|
|
28217
28347
|
const entryTypes = collection.entries ?? [];
|
|
28218
28348
|
let listed;
|
|
28219
28349
|
try {
|
|
28220
|
-
listed = await listCollectionEntries2(root, collection, (file) => result.malformed.push(
|
|
28350
|
+
listed = await listCollectionEntries2(root, collection, (file) => result.malformed.push(path61.posix.join(file.collectionPath, file.filename)));
|
|
28221
28351
|
} catch (err2) {
|
|
28222
28352
|
result.failed.push({ path: `${collection.logicalPath}/`, error: getErrorMessage(err2) });
|
|
28223
28353
|
continue;
|
|
@@ -28246,7 +28376,7 @@ async function backfillItemKeys(options) {
|
|
|
28246
28376
|
}
|
|
28247
28377
|
async function backfillEntry(store, root, item, fields, check) {
|
|
28248
28378
|
const doc = await store.read(item.collectionPath, item.slug, { resolveReferences: false });
|
|
28249
|
-
if (doc.absolutePath !==
|
|
28379
|
+
if (doc.absolutePath !== path61.resolve(root, item.physicalPath)) {
|
|
28250
28380
|
throw new Error(`Another file in its collection has the slug "${item.slug}", so neither a save nor the backfill can address this one. Rename or remove one of them.`);
|
|
28251
28381
|
}
|
|
28252
28382
|
const before = await fs46.readFile(doc.absolutePath, "utf8");
|
|
@@ -28332,7 +28462,7 @@ async function backfillItemKeysCLI(options) {
|
|
|
28332
28462
|
}
|
|
28333
28463
|
let flatSchema;
|
|
28334
28464
|
try {
|
|
28335
|
-
const contentDir =
|
|
28465
|
+
const contentDir = path61.join(projectDir, contentRootName);
|
|
28336
28466
|
const resolved = await resolveSchema2(contentDir, registry3, {
|
|
28337
28467
|
unknownSchema: "degrade"
|
|
28338
28468
|
});
|
|
@@ -28353,7 +28483,7 @@ async function backfillItemKeysCLI(options) {
|
|
|
28353
28483
|
return BACKFILL_EXIT_CODES.changesOrRefused;
|
|
28354
28484
|
}
|
|
28355
28485
|
}
|
|
28356
|
-
const metaDir =
|
|
28486
|
+
const metaDir = path61.join(projectDir, CANOPY_META_DIR2);
|
|
28357
28487
|
const metaDirExisted = await filePathExists(metaDir);
|
|
28358
28488
|
let result;
|
|
28359
28489
|
try {
|