canopycms 0.0.59 → 0.0.61-int.74
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/api/__test__/mock-client.d.ts +29 -0
- package/dist/api/__test__/mock-client.d.ts.map +1 -1
- package/dist/api/__test__/mock-client.js +55 -4
- package/dist/api/__test__/mock-client.js.map +1 -1
- package/dist/api/admin-branch-health.d.ts +65 -0
- package/dist/api/admin-branch-health.d.ts.map +1 -0
- package/dist/api/admin-branch-health.js +448 -0
- package/dist/api/admin-branch-health.js.map +1 -0
- package/dist/api/admin.d.ts +136 -0
- package/dist/api/admin.d.ts.map +1 -0
- package/dist/api/admin.js +338 -0
- package/dist/api/admin.js.map +1 -0
- package/dist/api/assets.d.ts.map +1 -1
- package/dist/api/assets.js +4 -0
- package/dist/api/assets.js.map +1 -1
- package/dist/api/branch-merge.d.ts +6 -0
- package/dist/api/branch-merge.d.ts.map +1 -1
- package/dist/api/branch-merge.js +15 -9
- package/dist/api/branch-merge.js.map +1 -1
- package/dist/api/branch-status.d.ts.map +1 -1
- package/dist/api/branch-status.js +15 -5
- package/dist/api/branch-status.js.map +1 -1
- package/dist/api/branch-withdraw.d.ts +6 -0
- package/dist/api/branch-withdraw.d.ts.map +1 -1
- package/dist/api/branch-withdraw.js +34 -7
- package/dist/api/branch-withdraw.js.map +1 -1
- package/dist/api/branch.d.ts +11 -1
- package/dist/api/branch.d.ts.map +1 -1
- package/dist/api/branch.js +70 -4
- package/dist/api/branch.js.map +1 -1
- package/dist/api/client.d.ts +34 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +47 -0
- package/dist/api/client.js.map +1 -1
- package/dist/api/comments.d.ts.map +1 -1
- package/dist/api/comments.js +4 -0
- package/dist/api/comments.js.map +1 -1
- package/dist/api/content.d.ts.map +1 -1
- package/dist/api/content.js +4 -4
- package/dist/api/content.js.map +1 -1
- package/dist/api/entries.d.ts.map +1 -1
- package/dist/api/entries.js +23 -4
- package/dist/api/entries.js.map +1 -1
- package/dist/api/github-sync.d.ts.map +1 -1
- package/dist/api/github-sync.js +14 -3
- package/dist/api/github-sync.js.map +1 -1
- package/dist/api/groups.d.ts +3 -0
- package/dist/api/groups.d.ts.map +1 -1
- package/dist/api/groups.js +89 -68
- package/dist/api/groups.js.map +1 -1
- package/dist/api/guards.d.ts +7 -1
- package/dist/api/guards.d.ts.map +1 -1
- package/dist/api/guards.js +54 -0
- package/dist/api/guards.js.map +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +0 -1
- package/dist/api/index.js.map +1 -1
- package/dist/api/permissions.d.ts +1 -0
- package/dist/api/permissions.d.ts.map +1 -1
- package/dist/api/permissions.js +23 -20
- package/dist/api/permissions.js.map +1 -1
- package/dist/api/schema.d.ts.map +1 -1
- package/dist/api/schema.js +36 -22
- package/dist/api/schema.js.map +1 -1
- package/dist/authorization/branch.d.ts +14 -1
- package/dist/authorization/branch.d.ts.map +1 -1
- package/dist/authorization/branch.js +5 -3
- package/dist/authorization/branch.js.map +1 -1
- package/dist/authorization/groups/index.d.ts +1 -1
- package/dist/authorization/groups/index.d.ts.map +1 -1
- package/dist/authorization/groups/index.js +1 -1
- package/dist/authorization/groups/index.js.map +1 -1
- package/dist/authorization/groups/loader.d.ts +25 -4
- package/dist/authorization/groups/loader.d.ts.map +1 -1
- package/dist/authorization/groups/loader.js +39 -20
- package/dist/authorization/groups/loader.js.map +1 -1
- package/dist/authorization/groups/schema.d.ts +8 -7
- package/dist/authorization/groups/schema.d.ts.map +1 -1
- package/dist/authorization/groups/schema.js +8 -4
- package/dist/authorization/groups/schema.js.map +1 -1
- package/dist/authorization/index.d.ts +5 -3
- package/dist/authorization/index.d.ts.map +1 -1
- package/dist/authorization/index.js +8 -3
- package/dist/authorization/index.js.map +1 -1
- package/dist/authorization/path.d.ts +8 -0
- package/dist/authorization/path.d.ts.map +1 -1
- package/dist/authorization/path.js +16 -1
- package/dist/authorization/path.js.map +1 -1
- package/dist/authorization/permissions/index.d.ts +1 -1
- package/dist/authorization/permissions/index.d.ts.map +1 -1
- package/dist/authorization/permissions/index.js +1 -1
- package/dist/authorization/permissions/index.js.map +1 -1
- package/dist/authorization/permissions/loader.d.ts +18 -4
- package/dist/authorization/permissions/loader.d.ts.map +1 -1
- package/dist/authorization/permissions/loader.js +35 -24
- package/dist/authorization/permissions/loader.js.map +1 -1
- package/dist/authorization/permissions/schema.d.ts +8 -7
- package/dist/authorization/permissions/schema.d.ts.map +1 -1
- package/dist/authorization/permissions/schema.js +8 -4
- package/dist/authorization/permissions/schema.js.map +1 -1
- package/dist/authorization/protected-branch.d.ts +39 -0
- package/dist/authorization/protected-branch.d.ts.map +1 -0
- package/dist/authorization/protected-branch.js +43 -0
- package/dist/authorization/protected-branch.js.map +1 -0
- package/dist/authorization/settings-file-store.d.ts +127 -0
- package/dist/authorization/settings-file-store.d.ts.map +1 -0
- package/dist/authorization/settings-file-store.js +156 -0
- package/dist/authorization/settings-file-store.js.map +1 -0
- package/dist/branch-health.d.ts +78 -0
- package/dist/branch-health.d.ts.map +1 -0
- package/dist/branch-health.js +151 -0
- package/dist/branch-health.js.map +1 -0
- package/dist/branch-metadata.d.ts +31 -0
- package/dist/branch-metadata.d.ts.map +1 -1
- package/dist/branch-metadata.js +43 -1
- package/dist/branch-metadata.js.map +1 -1
- package/dist/branch-registry.d.ts.map +1 -1
- package/dist/branch-registry.js +13 -1
- package/dist/branch-registry.js.map +1 -1
- package/dist/cli/cli.js +398 -200
- package/dist/cli/generate-ai-content.js +72 -13
- package/dist/cli/init.js +84 -2
- package/dist/cli/migrate.d.ts.map +1 -1
- package/dist/cli/migrate.js +39 -11
- package/dist/cli/migrate.js.map +1 -1
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +4 -1
- package/dist/cli/sync.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/schemas/config.d.ts +55 -5
- package/dist/config/schemas/config.d.ts.map +1 -1
- package/dist/config/schemas/config.js +18 -1
- package/dist/config/schemas/config.js.map +1 -1
- package/dist/config/types.d.ts +11 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/content-store.d.ts +16 -0
- package/dist/content-store.d.ts.map +1 -1
- package/dist/content-store.js +22 -6
- package/dist/content-store.js.map +1 -1
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +25 -3
- package/dist/context.js.map +1 -1
- package/dist/editor/BranchManager.d.ts +11 -0
- package/dist/editor/BranchManager.d.ts.map +1 -1
- package/dist/editor/BranchManager.js +34 -10
- package/dist/editor/BranchManager.js.map +1 -1
- package/dist/editor/CommentsPanel.d.ts.map +1 -1
- package/dist/editor/CommentsPanel.js +4 -12
- package/dist/editor/CommentsPanel.js.map +1 -1
- package/dist/editor/Editor.d.ts.map +1 -1
- package/dist/editor/Editor.js +46 -10
- package/dist/editor/Editor.js.map +1 -1
- package/dist/editor/EntryNavigator.d.ts +7 -0
- package/dist/editor/EntryNavigator.d.ts.map +1 -1
- package/dist/editor/EntryNavigator.js +17 -5
- package/dist/editor/EntryNavigator.js.map +1 -1
- package/dist/editor/FormRenderer.d.ts.map +1 -1
- package/dist/editor/FormRenderer.js +4 -0
- package/dist/editor/FormRenderer.js.map +1 -1
- package/dist/editor/admin/SystemHealthPanel.d.ts +6 -0
- package/dist/editor/admin/SystemHealthPanel.d.ts.map +1 -0
- package/dist/editor/admin/SystemHealthPanel.js +234 -0
- package/dist/editor/admin/SystemHealthPanel.js.map +1 -0
- package/dist/editor/admin/useSystemHealth.d.ts +40 -0
- package/dist/editor/admin/useSystemHealth.d.ts.map +1 -0
- package/dist/editor/admin/useSystemHealth.js +205 -0
- package/dist/editor/admin/useSystemHealth.js.map +1 -0
- package/dist/editor/comments/FieldWrapper.d.ts.map +1 -1
- package/dist/editor/comments/FieldWrapper.js +1 -1
- package/dist/editor/comments/FieldWrapper.js.map +1 -1
- package/dist/editor/comments/InlineCommentThread.d.ts.map +1 -1
- package/dist/editor/comments/InlineCommentThread.js +2 -21
- package/dist/editor/comments/InlineCommentThread.js.map +1 -1
- package/dist/editor/comments/ThreadCarousel.d.ts +2 -0
- package/dist/editor/comments/ThreadCarousel.d.ts.map +1 -1
- package/dist/editor/comments/ThreadCarousel.js +5 -2
- package/dist/editor/comments/ThreadCarousel.js.map +1 -1
- package/dist/editor/components/EditorHeader.d.ts +11 -0
- package/dist/editor/components/EditorHeader.d.ts.map +1 -1
- package/dist/editor/components/EditorHeader.js +75 -41
- package/dist/editor/components/EditorHeader.js.map +1 -1
- package/dist/editor/components/EditorSidebar.d.ts +7 -1
- package/dist/editor/components/EditorSidebar.d.ts.map +1 -1
- package/dist/editor/components/EditorSidebar.js +3 -3
- package/dist/editor/components/EditorSidebar.js.map +1 -1
- package/dist/editor/fields/ImageField.d.ts.map +1 -1
- package/dist/editor/fields/ImageField.js +4 -1
- package/dist/editor/fields/ImageField.js.map +1 -1
- package/dist/editor/fields/StringListField.d.ts +20 -0
- package/dist/editor/fields/StringListField.d.ts.map +1 -0
- package/dist/editor/fields/StringListField.js +25 -0
- package/dist/editor/fields/StringListField.js.map +1 -0
- package/dist/editor/hooks/useBranchActions.d.ts.map +1 -1
- package/dist/editor/hooks/useBranchActions.js +8 -2
- package/dist/editor/hooks/useBranchActions.js.map +1 -1
- package/dist/editor/hooks/useBranchManager.d.ts +14 -3
- package/dist/editor/hooks/useBranchManager.d.ts.map +1 -1
- package/dist/editor/hooks/useBranchManager.js +18 -1
- package/dist/editor/hooks/useBranchManager.js.map +1 -1
- package/dist/editor/hooks/useDraftManager.d.ts +7 -0
- package/dist/editor/hooks/useDraftManager.d.ts.map +1 -1
- package/dist/editor/hooks/useDraftManager.js +57 -3
- package/dist/editor/hooks/useDraftManager.js.map +1 -1
- package/dist/editor/hooks/useEntryManager.d.ts.map +1 -1
- package/dist/editor/hooks/useEntryManager.js +25 -7
- package/dist/editor/hooks/useEntryManager.js.map +1 -1
- package/dist/editor/hooks/useGroupManager.d.ts.map +1 -1
- package/dist/editor/hooks/useGroupManager.js +13 -2
- package/dist/editor/hooks/useGroupManager.js.map +1 -1
- package/dist/editor/hooks/usePermissionManager.d.ts.map +1 -1
- package/dist/editor/hooks/usePermissionManager.js +13 -2
- package/dist/editor/hooks/usePermissionManager.js.map +1 -1
- package/dist/editor/media/AssetCard.d.ts.map +1 -1
- package/dist/editor/media/AssetCard.js +23 -2
- package/dist/editor/media/AssetCard.js.map +1 -1
- package/dist/editor/relative-time.d.ts +8 -0
- package/dist/editor/relative-time.d.ts.map +1 -0
- package/dist/editor/relative-time.js +26 -0
- package/dist/editor/relative-time.js.map +1 -0
- package/dist/git-manager.d.ts.map +1 -1
- package/dist/git-manager.js +16 -2
- package/dist/git-manager.js.map +1 -1
- package/dist/http/handler.d.ts.map +1 -1
- package/dist/http/handler.js +20 -9
- package/dist/http/handler.js.map +1 -1
- package/dist/http/router.d.ts.map +1 -1
- package/dist/http/router.js +2 -0
- package/dist/http/router.js.map +1 -1
- package/dist/paths/branch-name.d.ts +20 -0
- package/dist/paths/branch-name.d.ts.map +1 -0
- package/dist/paths/branch-name.js +24 -0
- package/dist/paths/branch-name.js.map +1 -0
- package/dist/paths/branch.d.ts +2 -8
- package/dist/paths/branch.d.ts.map +1 -1
- package/dist/paths/branch.js +6 -12
- package/dist/paths/branch.js.map +1 -1
- package/dist/paths/index.d.ts +2 -1
- package/dist/paths/index.d.ts.map +1 -1
- package/dist/paths/index.js +4 -1
- package/dist/paths/index.js.map +1 -1
- package/dist/schema/schema-store.d.ts +98 -0
- package/dist/schema/schema-store.d.ts.map +1 -1
- package/dist/schema/schema-store.js +187 -29
- package/dist/schema/schema-store.js.map +1 -1
- package/dist/services.js +9 -1
- package/dist/services.js.map +1 -1
- package/dist/task-queue/index.d.ts +2 -2
- package/dist/task-queue/index.d.ts.map +1 -1
- package/dist/task-queue/index.js +1 -1
- package/dist/task-queue/index.js.map +1 -1
- package/dist/task-queue/task-queue.d.ts +37 -1
- package/dist/task-queue/task-queue.d.ts.map +1 -1
- package/dist/task-queue/task-queue.js +104 -0
- package/dist/task-queue/task-queue.js.map +1 -1
- package/dist/task-queue/types.d.ts +11 -0
- package/dist/task-queue/types.d.ts.map +1 -1
- package/dist/types.d.ts +54 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/occ-json-write.d.ts.map +1 -1
- package/dist/utils/occ-json-write.js +7 -0
- package/dist/utils/occ-json-write.js.map +1 -1
- package/dist/utils/provisioning-lock.d.ts +19 -0
- package/dist/utils/provisioning-lock.d.ts.map +1 -1
- package/dist/utils/provisioning-lock.js +26 -0
- package/dist/utils/provisioning-lock.js.map +1 -1
- package/dist/validation/reference-validator.d.ts +17 -1
- package/dist/validation/reference-validator.d.ts.map +1 -1
- package/dist/validation/reference-validator.js +33 -10
- package/dist/validation/reference-validator.js.map +1 -1
- package/dist/worker/cms-worker.d.ts +74 -0
- package/dist/worker/cms-worker.d.ts.map +1 -1
- package/dist/worker/cms-worker.js +542 -29
- package/dist/worker/cms-worker.js.map +1 -1
- package/dist/worker/task-queue.d.ts +3 -3
- package/dist/worker/task-queue.d.ts.map +1 -1
- package/dist/worker/task-queue.js +2 -2
- package/dist/worker/task-queue.js.map +1 -1
- package/dist/worker/worker-status.d.ts +48 -0
- package/dist/worker/worker-status.d.ts.map +1 -0
- package/dist/worker/worker-status.js +53 -0
- package/dist/worker/worker-status.js.map +1 -0
- package/package.json +1 -1
package/dist/cli/cli.js
CHANGED
|
@@ -362,6 +362,36 @@ async function retryTask(taskDir, taskId, error, logger = nullLogger) {
|
|
|
362
362
|
});
|
|
363
363
|
logger.debug("Retrying task", { id: taskId, retryCount, backoffMs });
|
|
364
364
|
}
|
|
365
|
+
async function requeueFailedTask(taskDir, taskId, logger = nullLogger) {
|
|
366
|
+
const failedPath = path5.join(taskDir, "failed", `${taskId}.json`);
|
|
367
|
+
let content;
|
|
368
|
+
try {
|
|
369
|
+
content = await fs4.readFile(failedPath, "utf-8");
|
|
370
|
+
} catch (err) {
|
|
371
|
+
if (isNotFoundError2(err))
|
|
372
|
+
return { error: "not-found" };
|
|
373
|
+
throw err;
|
|
374
|
+
}
|
|
375
|
+
const original = parseTaskJson(content);
|
|
376
|
+
if (!original)
|
|
377
|
+
return { error: "unparseable" };
|
|
378
|
+
const newTaskId = crypto.randomUUID();
|
|
379
|
+
const newTask = {
|
|
380
|
+
id: newTaskId,
|
|
381
|
+
action: original.action,
|
|
382
|
+
payload: { ...original.payload, requeuedFrom: taskId },
|
|
383
|
+
status: "pending",
|
|
384
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
385
|
+
retryCount: 0,
|
|
386
|
+
maxRetries: original.maxRetries
|
|
387
|
+
};
|
|
388
|
+
const pendingPath = path5.join(taskDir, "pending", `${newTaskId}.json`);
|
|
389
|
+
await atomicWriteFile(pendingPath, JSON.stringify(newTask, null, 2));
|
|
390
|
+
await fs4.unlink(failedPath).catch(() => {
|
|
391
|
+
});
|
|
392
|
+
logger.debug("Requeued failed task", { originalId: taskId, newTaskId });
|
|
393
|
+
return { newTaskId };
|
|
394
|
+
}
|
|
365
395
|
async function recoverOrphanedTasks(taskDir, maxAgeMs = 5 * 6e4, logger = nullLogger) {
|
|
366
396
|
const processingDir = path5.join(taskDir, "processing");
|
|
367
397
|
const pendingDir = path5.join(taskDir, "pending");
|
|
@@ -491,6 +521,46 @@ async function getQueueStats(taskDir) {
|
|
|
491
521
|
}
|
|
492
522
|
return stats;
|
|
493
523
|
}
|
|
524
|
+
async function listCorruptTaskFiles(taskDir, limit = 50, logger = nullLogger) {
|
|
525
|
+
const corruptDir = path5.join(taskDir, "corrupt");
|
|
526
|
+
let files;
|
|
527
|
+
try {
|
|
528
|
+
files = await fs4.readdir(corruptDir);
|
|
529
|
+
} catch (err) {
|
|
530
|
+
if (isNotFoundError2(err))
|
|
531
|
+
return [];
|
|
532
|
+
throw err;
|
|
533
|
+
}
|
|
534
|
+
const entries = [];
|
|
535
|
+
for (const fileName of files.filter((f) => f.endsWith(".json"))) {
|
|
536
|
+
const filePath = path5.join(corruptDir, fileName);
|
|
537
|
+
try {
|
|
538
|
+
const stat = await fs4.stat(filePath);
|
|
539
|
+
entries.push({
|
|
540
|
+
fileName,
|
|
541
|
+
size: stat.size,
|
|
542
|
+
mtime: stat.mtime.toISOString(),
|
|
543
|
+
rawSnippet: await readSnippet(filePath, 500)
|
|
544
|
+
});
|
|
545
|
+
} catch (err) {
|
|
546
|
+
if (isNotFoundError2(err))
|
|
547
|
+
continue;
|
|
548
|
+
logger.debug("Failed to read corrupt task file", { fileName });
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
entries.sort((a, b) => new Date(b.mtime).getTime() - new Date(a.mtime).getTime());
|
|
552
|
+
return entries.slice(0, limit);
|
|
553
|
+
}
|
|
554
|
+
async function readSnippet(filePath, maxBytes) {
|
|
555
|
+
const handle = await fs4.open(filePath, "r");
|
|
556
|
+
try {
|
|
557
|
+
const buffer = Buffer.alloc(maxBytes);
|
|
558
|
+
const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
|
|
559
|
+
return buffer.subarray(0, bytesRead).toString("utf-8");
|
|
560
|
+
} finally {
|
|
561
|
+
await handle.close();
|
|
562
|
+
}
|
|
563
|
+
}
|
|
494
564
|
function parseTaskJson(content) {
|
|
495
565
|
try {
|
|
496
566
|
const parsed = JSON.parse(content);
|
|
@@ -794,7 +864,7 @@ CanopyCMS worker run-once (mode: ${mode}, auth: ${authMode})
|
|
|
794
864
|
}
|
|
795
865
|
console.log("\nDone");
|
|
796
866
|
}
|
|
797
|
-
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, 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;
|
|
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;
|
|
798
868
|
var init_init = __esm({
|
|
799
869
|
"dist/cli/init.js"() {
|
|
800
870
|
"use strict";
|
|
@@ -980,7 +1050,13 @@ var init_init = __esm({
|
|
|
980
1050
|
init_collection();
|
|
981
1051
|
init_media();
|
|
982
1052
|
defaultBranchAccessSchema = z4.enum(["allow", "deny"]).default("deny");
|
|
983
|
-
|
|
1053
|
+
defaultPathAccessLevelSchema = z4.enum(["allow", "deny"]);
|
|
1054
|
+
defaultPathAccessLevelsSchema = z4.object({
|
|
1055
|
+
read: defaultPathAccessLevelSchema.optional(),
|
|
1056
|
+
edit: defaultPathAccessLevelSchema.optional(),
|
|
1057
|
+
review: defaultPathAccessLevelSchema.optional()
|
|
1058
|
+
}).strict();
|
|
1059
|
+
defaultPathAccessSchema = z4.union([defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema]).default("deny");
|
|
984
1060
|
defaultBaseBranchSchema = z4.string().default("main");
|
|
985
1061
|
defaultRemoteNameSchema = z4.string().default("origin");
|
|
986
1062
|
defaultRemoteUrlSchema = z4.string().min(1);
|
|
@@ -1013,6 +1089,10 @@ var init_init = __esm({
|
|
|
1013
1089
|
// have .default('deny'), so the field is optional on input but always resolves to
|
|
1014
1090
|
// 'allow'/'deny' (never undefined) on output. An outer .optional() would short-circuit
|
|
1015
1091
|
// before the inner default runs, defeating the fail-closed default (SCH-M1).
|
|
1092
|
+
// defaultPathAccessSchema's union keeps the same rule: the .default('deny') sits on the
|
|
1093
|
+
// OUTER union, not inside defaultPathAccessLevelsSchema, so an omitted top-level field
|
|
1094
|
+
// still resolves to 'deny' and an omitted level inside the object form stays undefined
|
|
1095
|
+
// (resolved to 'deny' at read time by resolveDefaultPathAccess).
|
|
1016
1096
|
defaultBranchAccess: defaultBranchAccessSchema,
|
|
1017
1097
|
defaultPathAccess: defaultPathAccessSchema,
|
|
1018
1098
|
// .optional() deliberately defeats defaultBaseBranchSchema's .default('main'):
|
|
@@ -1261,8 +1341,10 @@ var init_init = __esm({
|
|
|
1261
1341
|
getQueueStats: () => getQueueStats,
|
|
1262
1342
|
getTask: () => getTask,
|
|
1263
1343
|
getTaskResult: () => getTask,
|
|
1344
|
+
listCorruptTaskFiles: () => listCorruptTaskFiles,
|
|
1264
1345
|
listTasks: () => listTasks,
|
|
1265
1346
|
recoverOrphanedTasks: () => recoverOrphanedTasks,
|
|
1347
|
+
requeueFailedTask: () => requeueFailedTask,
|
|
1266
1348
|
retryTask: () => retryTask
|
|
1267
1349
|
});
|
|
1268
1350
|
init_task_queue3 = __esm2({
|
|
@@ -3046,6 +3128,8 @@ async function withOccFileLock(filePath, fn) {
|
|
|
3046
3128
|
return await fn();
|
|
3047
3129
|
} finally {
|
|
3048
3130
|
await release().catch((err) => {
|
|
3131
|
+
if (isNodeError3(err) && err.code === "ENOENT")
|
|
3132
|
+
return;
|
|
3049
3133
|
log4.warn("lock", `Failed to release lock for ${filePath}`, { error: getErrorMessage3(err) });
|
|
3050
3134
|
});
|
|
3051
3135
|
}
|
|
@@ -3560,7 +3644,7 @@ async function generateAIContentCLI(options) {
|
|
|
3560
3644
|
console.log(` Output: ${result.outputDir}
|
|
3561
3645
|
`);
|
|
3562
3646
|
}
|
|
3563
|
-
var __defProp3, __getOwnPropNames3, __esm3, __export3, init_normalize2, ROOT_COLLECTION_ID, init_types3, normalizePathValue, flattenSchema, init_flatten2, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, 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, defaultPathAccessSchema2, defaultBaseBranchSchema2, defaultRemoteNameSchema2, defaultRemoteUrlSchema2, gitBotAuthorNameSchema2, gitBotAuthorEmailSchema2, githubTokenEnvVarSchema2, operatingModeSchema2, deployedAsSchema2, contentRootSchema2, sourceRootSchema2, deploymentNameSchema2, devContentSyncSchema2, devConfigSchema2, editorConfigSchema2, CanopyConfigSchema2, DEFAULT_PROD_WORKSPACE2, init_config3, permissionTargetSchema2, pathPermissionSchema2, init_permissions2, init_validation2, 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, 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, BranchMetadataFileManager, getBranchMetadataFileManager, loadBranchContext, NETWORK_SCHEME_PATTERN, TRANSPORT_HELPER_PATTERN, LEADING_OPTION_PATTERN, WINDOWS_DRIVE_PATTERN, COLON_BEFORE_SLASH_PATTERN, log5, GIT_ENV_PASSTHROUGH, remoteInitLocks, GitConflictError, GitManager, log6, workspaceInitLocks, BranchWorkspaceManager, log7, normalizePath, readEntryData, parseTypedFilename, listCollectionEntries, MAX_INPUT_PIXELS, KNOWN_FIELD_TYPES, STRING_FIELD_TYPES, selectOptionValues, jiti;
|
|
3647
|
+
var __defProp3, __getOwnPropNames3, __esm3, __export3, init_normalize2, ROOT_COLLECTION_ID, init_types3, normalizePathValue, flattenSchema, init_flatten2, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, 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, permissionTargetSchema2, pathPermissionSchema2, init_permissions2, init_validation2, 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, 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, remoteInitLocks, GitConflictError, GitManager, log6, workspaceInitLocks, BranchWorkspaceManager, log7, normalizePath, readEntryData, parseTypedFilename, listCollectionEntries, MAX_INPUT_PIXELS, KNOWN_FIELD_TYPES, STRING_FIELD_TYPES, selectOptionValues, jiti;
|
|
3564
3648
|
var init_generate_ai_content = __esm({
|
|
3565
3649
|
"dist/cli/generate-ai-content.js"() {
|
|
3566
3650
|
"use strict";
|
|
@@ -3924,7 +4008,13 @@ var init_generate_ai_content = __esm({
|
|
|
3924
4008
|
init_collection2();
|
|
3925
4009
|
init_media2();
|
|
3926
4010
|
defaultBranchAccessSchema2 = z52.enum(["allow", "deny"]).default("deny");
|
|
3927
|
-
|
|
4011
|
+
defaultPathAccessLevelSchema2 = z52.enum(["allow", "deny"]);
|
|
4012
|
+
defaultPathAccessLevelsSchema2 = z52.object({
|
|
4013
|
+
read: defaultPathAccessLevelSchema2.optional(),
|
|
4014
|
+
edit: defaultPathAccessLevelSchema2.optional(),
|
|
4015
|
+
review: defaultPathAccessLevelSchema2.optional()
|
|
4016
|
+
}).strict();
|
|
4017
|
+
defaultPathAccessSchema2 = z52.union([defaultPathAccessLevelSchema2, defaultPathAccessLevelsSchema2]).default("deny");
|
|
3928
4018
|
defaultBaseBranchSchema2 = z52.string().default("main");
|
|
3929
4019
|
defaultRemoteNameSchema2 = z52.string().default("origin");
|
|
3930
4020
|
defaultRemoteUrlSchema2 = z52.string().min(1);
|
|
@@ -3957,6 +4047,10 @@ var init_generate_ai_content = __esm({
|
|
|
3957
4047
|
// have .default('deny'), so the field is optional on input but always resolves to
|
|
3958
4048
|
// 'allow'/'deny' (never undefined) on output. An outer .optional() would short-circuit
|
|
3959
4049
|
// before the inner default runs, defeating the fail-closed default (SCH-M1).
|
|
4050
|
+
// defaultPathAccessSchema's union keeps the same rule: the .default('deny') sits on the
|
|
4051
|
+
// OUTER union, not inside defaultPathAccessLevelsSchema, so an omitted top-level field
|
|
4052
|
+
// still resolves to 'deny' and an omitted level inside the object form stays undefined
|
|
4053
|
+
// (resolved to 'deny' at read time by resolveDefaultPathAccess).
|
|
3960
4054
|
defaultBranchAccess: defaultBranchAccessSchema2,
|
|
3961
4055
|
defaultPathAccess: defaultPathAccessSchema2,
|
|
3962
4056
|
// .optional() deliberately defeats defaultBaseBranchSchema's .default('main'):
|
|
@@ -5521,8 +5615,22 @@ var init_generate_ai_content = __esm({
|
|
|
5521
5615
|
* Returns array of entry metadata (relativePath, collection, slug).
|
|
5522
5616
|
* Returns empty array if the collection doesn't exist.
|
|
5523
5617
|
*/
|
|
5524
|
-
|
|
5525
|
-
|
|
5618
|
+
/**
|
|
5619
|
+
* Resolve a schema collection referenced by name or logical path.
|
|
5620
|
+
*
|
|
5621
|
+
* Accepts either a full logical path ("content/authors") or a bare
|
|
5622
|
+
* collection name ("authors") — the contract reference fields use for
|
|
5623
|
+
* `collections: [...]` (see README). This is the single normalization
|
|
5624
|
+
* point shared by reference-option loading and reference validation so
|
|
5625
|
+
* the dropdown and the write boundary can never disagree.
|
|
5626
|
+
*
|
|
5627
|
+
* CAVEAT: the bare-name fallback matches on the LAST path segment and
|
|
5628
|
+
* returns the first hit in schema order — with two collections sharing a
|
|
5629
|
+
* leaf name (e.g. content/blog/posts and content/news/posts), a bare
|
|
5630
|
+
* 'posts' is ambiguous. Use the full logical path in schemas that nest
|
|
5631
|
+
* same-named collections.
|
|
5632
|
+
*/
|
|
5633
|
+
resolveCollectionItem(collectionPath) {
|
|
5526
5634
|
const normalized = normalizeFilesystemPath(collectionPath);
|
|
5527
5635
|
let item = this.schemaIndex.get(normalized);
|
|
5528
5636
|
if (!item) {
|
|
@@ -5536,10 +5644,14 @@ var init_generate_ai_content = __esm({
|
|
|
5536
5644
|
}
|
|
5537
5645
|
}
|
|
5538
5646
|
}
|
|
5539
|
-
|
|
5647
|
+
return item && item.type === "collection" ? item : void 0;
|
|
5648
|
+
}
|
|
5649
|
+
async getCollectionEntryPaths(collectionPath) {
|
|
5650
|
+
const idIndex = await this.idIndex();
|
|
5651
|
+
const collection = this.resolveCollectionItem(collectionPath);
|
|
5652
|
+
if (!collection) {
|
|
5540
5653
|
return [];
|
|
5541
5654
|
}
|
|
5542
|
-
const collection = item;
|
|
5543
5655
|
const treeEntries = idIndex.getEntriesInCollectionTree(collection.logicalPath);
|
|
5544
5656
|
const entries = [];
|
|
5545
5657
|
for (const location of treeEntries) {
|
|
@@ -5817,7 +5929,13 @@ var init_generate_ai_content = __esm({
|
|
|
5817
5929
|
continue;
|
|
5818
5930
|
}
|
|
5819
5931
|
const branchRoot = path10.join(this.root, entry.name);
|
|
5820
|
-
|
|
5932
|
+
let meta;
|
|
5933
|
+
try {
|
|
5934
|
+
meta = await BranchMetadataFileManager.loadOnly(branchRoot);
|
|
5935
|
+
} catch (err) {
|
|
5936
|
+
console.warn(`CanopyCMS: Skipping branch directory '${entry.name}' during registry scan: ${getErrorMessage3(err)}`);
|
|
5937
|
+
continue;
|
|
5938
|
+
}
|
|
5821
5939
|
if (meta) {
|
|
5822
5940
|
branches.push({
|
|
5823
5941
|
branch: meta.branch,
|
|
@@ -5851,6 +5969,14 @@ var init_generate_ai_content = __esm({
|
|
|
5851
5969
|
this.name = "BranchMetadataConflictError";
|
|
5852
5970
|
}
|
|
5853
5971
|
};
|
|
5972
|
+
BranchMetadataCorruptError = class extends Error {
|
|
5973
|
+
constructor(branchRoot, cause) {
|
|
5974
|
+
super(`Corrupt branch metadata in '${branchRoot}': ${cause}`);
|
|
5975
|
+
this.name = "BranchMetadataCorruptError";
|
|
5976
|
+
this.branchRoot = branchRoot;
|
|
5977
|
+
this.parseCause = cause;
|
|
5978
|
+
}
|
|
5979
|
+
};
|
|
5854
5980
|
BranchMetadataFileManager = class _BranchMetadataFileManager {
|
|
5855
5981
|
constructor(branchRoot, baseRoot, options) {
|
|
5856
5982
|
this.branchRoot = path12.resolve(branchRoot);
|
|
@@ -5863,7 +5989,8 @@ var init_generate_ai_content = __esm({
|
|
|
5863
5989
|
* Use this for read-only access (e.g., in registry scanning or loadBranchContext).
|
|
5864
5990
|
*/
|
|
5865
5991
|
static async loadOnly(branchRoot) {
|
|
5866
|
-
const
|
|
5992
|
+
const resolvedRoot = path12.resolve(branchRoot);
|
|
5993
|
+
const filePath = path12.join(resolvedRoot, BRANCH_META_DIR, BRANCH_META_FILE);
|
|
5867
5994
|
try {
|
|
5868
5995
|
const raw = await fs12.readFile(filePath, "utf8");
|
|
5869
5996
|
return JSON.parse(raw);
|
|
@@ -5871,6 +5998,9 @@ var init_generate_ai_content = __esm({
|
|
|
5871
5998
|
if (isNotFoundError4(err)) {
|
|
5872
5999
|
return null;
|
|
5873
6000
|
}
|
|
6001
|
+
if (err instanceof SyntaxError) {
|
|
6002
|
+
throw new BranchMetadataCorruptError(resolvedRoot, err.message);
|
|
6003
|
+
}
|
|
5874
6004
|
throw err;
|
|
5875
6005
|
}
|
|
5876
6006
|
}
|
|
@@ -5980,7 +6110,11 @@ var init_generate_ai_content = __esm({
|
|
|
5980
6110
|
createdBy: existing?.branch.createdBy ?? incoming.branch?.createdBy ?? defaults.createdBy,
|
|
5981
6111
|
createdAt: existing?.branch.createdAt ?? defaults.createdAt,
|
|
5982
6112
|
// Fork point is recorded once at creation; later saves must not move it
|
|
5983
|
-
baseBranch: existing?.branch.baseBranch ?? incoming.branch?.baseBranch
|
|
6113
|
+
baseBranch: existing?.branch.baseBranch ?? incoming.branch?.baseBranch,
|
|
6114
|
+
// Always stamped fresh — the spreads above would otherwise let
|
|
6115
|
+
// the existing (creation-time) value win forever, freezing the
|
|
6116
|
+
// timestamp the editor's Branches panel sorts and displays by
|
|
6117
|
+
updatedAt: now
|
|
5984
6118
|
}
|
|
5985
6119
|
};
|
|
5986
6120
|
const written = await this.write(merged, version);
|
|
@@ -6450,6 +6584,7 @@ var init_generate_ai_content = __esm({
|
|
|
6450
6584
|
}
|
|
6451
6585
|
if (options.branchType === "orphan") {
|
|
6452
6586
|
await git.createOrphanSettingsBranch(options.branchName, {});
|
|
6587
|
+
await git.ensureGitExclude("*.lock");
|
|
6453
6588
|
} else {
|
|
6454
6589
|
await git.checkoutBranch(options.branchName);
|
|
6455
6590
|
if (options.gitExcludePattern) {
|
|
@@ -6531,8 +6666,9 @@ var init_generate_ai_content = __esm({
|
|
|
6531
6666
|
}
|
|
6532
6667
|
async pullBaseInner() {
|
|
6533
6668
|
await this.git.fetch(this.remote, this.baseBranch);
|
|
6669
|
+
const fetchedTip = (await this.git.revparse(["FETCH_HEAD"])).trim();
|
|
6534
6670
|
try {
|
|
6535
|
-
await this.git.merge([
|
|
6671
|
+
await this.git.merge([fetchedTip]);
|
|
6536
6672
|
} catch (err) {
|
|
6537
6673
|
try {
|
|
6538
6674
|
const status = await this.git.status();
|
|
@@ -6587,8 +6723,9 @@ var init_generate_ai_content = __esm({
|
|
|
6587
6723
|
}
|
|
6588
6724
|
async rebaseOntoBaseInner() {
|
|
6589
6725
|
await this.git.fetch(this.remote, this.baseBranch);
|
|
6726
|
+
const fetchedTip = (await this.git.revparse(["FETCH_HEAD"])).trim();
|
|
6590
6727
|
try {
|
|
6591
|
-
await this.git.rebase([
|
|
6728
|
+
await this.git.rebase([fetchedTip]);
|
|
6592
6729
|
} catch (err) {
|
|
6593
6730
|
try {
|
|
6594
6731
|
const status = await this.git.status();
|
|
@@ -7875,14 +8012,20 @@ var init_media3 = __esm({
|
|
|
7875
8012
|
|
|
7876
8013
|
// dist/config/schemas/config.js
|
|
7877
8014
|
import { z as z12 } from "zod";
|
|
7878
|
-
var defaultBranchAccessSchema3, defaultPathAccessSchema3, defaultBaseBranchSchema3, defaultRemoteNameSchema3, defaultRemoteUrlSchema3, gitBotAuthorNameSchema3, gitBotAuthorEmailSchema3, githubTokenEnvVarSchema3, operatingModeSchema3, deployedAsSchema3, contentRootSchema3, sourceRootSchema3, deploymentNameSchema3, devContentSyncSchema3, devConfigSchema3, editorConfigSchema3, CanopyConfigSchema3, DEFAULT_PROD_WORKSPACE3;
|
|
8015
|
+
var defaultBranchAccessSchema3, defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3, defaultPathAccessSchema3, defaultBaseBranchSchema3, defaultRemoteNameSchema3, defaultRemoteUrlSchema3, gitBotAuthorNameSchema3, gitBotAuthorEmailSchema3, githubTokenEnvVarSchema3, operatingModeSchema3, deployedAsSchema3, contentRootSchema3, sourceRootSchema3, deploymentNameSchema3, devContentSyncSchema3, devConfigSchema3, editorConfigSchema3, CanopyConfigSchema3, DEFAULT_PROD_WORKSPACE3;
|
|
7879
8016
|
var init_config4 = __esm({
|
|
7880
8017
|
"dist/config/schemas/config.js"() {
|
|
7881
8018
|
"use strict";
|
|
7882
8019
|
init_collection3();
|
|
7883
8020
|
init_media3();
|
|
7884
8021
|
defaultBranchAccessSchema3 = z12.enum(["allow", "deny"]).default("deny");
|
|
7885
|
-
|
|
8022
|
+
defaultPathAccessLevelSchema3 = z12.enum(["allow", "deny"]);
|
|
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");
|
|
7886
8029
|
defaultBaseBranchSchema3 = z12.string().default("main");
|
|
7887
8030
|
defaultRemoteNameSchema3 = z12.string().default("origin");
|
|
7888
8031
|
defaultRemoteUrlSchema3 = z12.string().min(1);
|
|
@@ -7915,6 +8058,10 @@ var init_config4 = __esm({
|
|
|
7915
8058
|
// have .default('deny'), so the field is optional on input but always resolves to
|
|
7916
8059
|
// 'allow'/'deny' (never undefined) on output. An outer .optional() would short-circuit
|
|
7917
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).
|
|
7918
8065
|
defaultBranchAccess: defaultBranchAccessSchema3,
|
|
7919
8066
|
defaultPathAccess: defaultPathAccessSchema3,
|
|
7920
8067
|
// .optional() deliberately defeats defaultBaseBranchSchema's .default('main'):
|
|
@@ -8175,7 +8322,7 @@ async function selectBranch(options, branchesDir) {
|
|
|
8175
8322
|
let branches = [];
|
|
8176
8323
|
if (await filePathExists(branchesDir)) {
|
|
8177
8324
|
const entries = await fs23.readdir(branchesDir, { withFileTypes: true });
|
|
8178
|
-
branches = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
8325
|
+
branches = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
|
|
8179
8326
|
}
|
|
8180
8327
|
let branchName = options.branch;
|
|
8181
8328
|
if (!branchName) {
|
|
@@ -8554,11 +8701,19 @@ var init_resolve = __esm({
|
|
|
8554
8701
|
}
|
|
8555
8702
|
});
|
|
8556
8703
|
|
|
8704
|
+
// dist/paths/branch-name.js
|
|
8705
|
+
var init_branch_name = __esm({
|
|
8706
|
+
"dist/paths/branch-name.js"() {
|
|
8707
|
+
"use strict";
|
|
8708
|
+
}
|
|
8709
|
+
});
|
|
8710
|
+
|
|
8557
8711
|
// dist/paths/branch.js
|
|
8558
8712
|
var init_branch = __esm({
|
|
8559
8713
|
"dist/paths/branch.js"() {
|
|
8560
8714
|
"use strict";
|
|
8561
8715
|
init_operating_mode2();
|
|
8716
|
+
init_branch_name();
|
|
8562
8717
|
}
|
|
8563
8718
|
});
|
|
8564
8719
|
|
|
@@ -8571,6 +8726,7 @@ var init_paths = __esm({
|
|
|
8571
8726
|
init_validation3();
|
|
8572
8727
|
init_resolve();
|
|
8573
8728
|
init_branch();
|
|
8729
|
+
init_branch_name();
|
|
8574
8730
|
}
|
|
8575
8731
|
});
|
|
8576
8732
|
|
|
@@ -8583,10 +8739,170 @@ var init_schema = __esm({
|
|
|
8583
8739
|
}
|
|
8584
8740
|
});
|
|
8585
8741
|
|
|
8586
|
-
// dist/
|
|
8742
|
+
// dist/utils/async-mutex.js
|
|
8743
|
+
async function withLock2(key, fn) {
|
|
8744
|
+
const prev = locks2.get(key) ?? Promise.resolve();
|
|
8745
|
+
let resolve2;
|
|
8746
|
+
const next = new Promise((r) => {
|
|
8747
|
+
resolve2 = r;
|
|
8748
|
+
});
|
|
8749
|
+
locks2.set(key, next);
|
|
8750
|
+
await prev;
|
|
8751
|
+
try {
|
|
8752
|
+
return await fn();
|
|
8753
|
+
} finally {
|
|
8754
|
+
resolve2();
|
|
8755
|
+
if (locks2.get(key) === next)
|
|
8756
|
+
locks2.delete(key);
|
|
8757
|
+
}
|
|
8758
|
+
}
|
|
8759
|
+
var locks2;
|
|
8760
|
+
var init_async_mutex = __esm({
|
|
8761
|
+
"dist/utils/async-mutex.js"() {
|
|
8762
|
+
"use strict";
|
|
8763
|
+
locks2 = /* @__PURE__ */ new Map();
|
|
8764
|
+
}
|
|
8765
|
+
});
|
|
8766
|
+
|
|
8767
|
+
// dist/utils/occ-json-write.js
|
|
8587
8768
|
import fs24 from "node:fs/promises";
|
|
8588
8769
|
import path27 from "node:path";
|
|
8589
|
-
|
|
8770
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
8771
|
+
import lockfile4 from "proper-lockfile";
|
|
8772
|
+
async function writeOccJsonFile2(filePath, payload, options) {
|
|
8773
|
+
const { expectedVersion, settleMs = 50, trailingNewline = false } = options;
|
|
8774
|
+
const newVersion = expectedVersion === null ? 1 : expectedVersion + 1;
|
|
8775
|
+
const writeId = randomUUID4();
|
|
8776
|
+
const data = { ...payload, version: newVersion, writeId };
|
|
8777
|
+
const content = JSON.stringify(data, null, 2) + (trailingNewline ? "\n" : "");
|
|
8778
|
+
const dir = path27.dirname(filePath);
|
|
8779
|
+
await fs24.mkdir(dir, { recursive: true });
|
|
8780
|
+
const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
8781
|
+
await fs24.writeFile(tempPath, content, "utf-8");
|
|
8782
|
+
if (expectedVersion === null) {
|
|
8783
|
+
try {
|
|
8784
|
+
await fs24.link(tempPath, filePath);
|
|
8785
|
+
} catch (err) {
|
|
8786
|
+
await fs24.unlink(tempPath).catch(() => {
|
|
8787
|
+
});
|
|
8788
|
+
if (isFileExistsError(err)) {
|
|
8789
|
+
throw new OccWriteConflictError2();
|
|
8790
|
+
}
|
|
8791
|
+
throw err;
|
|
8792
|
+
}
|
|
8793
|
+
await fs24.unlink(tempPath).catch(() => {
|
|
8794
|
+
});
|
|
8795
|
+
return { version: newVersion, writeId };
|
|
8796
|
+
}
|
|
8797
|
+
try {
|
|
8798
|
+
let currentVersion = null;
|
|
8799
|
+
try {
|
|
8800
|
+
const current = JSON.parse(await fs24.readFile(filePath, "utf-8"));
|
|
8801
|
+
currentVersion = current.version ?? 0;
|
|
8802
|
+
} catch {
|
|
8803
|
+
currentVersion = null;
|
|
8804
|
+
}
|
|
8805
|
+
if (currentVersion !== expectedVersion) {
|
|
8806
|
+
throw new OccWriteConflictError2();
|
|
8807
|
+
}
|
|
8808
|
+
await fs24.rename(tempPath, filePath);
|
|
8809
|
+
if (settleMs > 0) {
|
|
8810
|
+
await new Promise((resolve2) => setTimeout(resolve2, settleMs));
|
|
8811
|
+
}
|
|
8812
|
+
const afterWrite = JSON.parse(await fs24.readFile(filePath, "utf-8"));
|
|
8813
|
+
if (afterWrite.writeId !== writeId) {
|
|
8814
|
+
throw new OccWriteConflictError2();
|
|
8815
|
+
}
|
|
8816
|
+
} catch (err) {
|
|
8817
|
+
await fs24.unlink(tempPath).catch(() => {
|
|
8818
|
+
});
|
|
8819
|
+
throw err;
|
|
8820
|
+
}
|
|
8821
|
+
return { version: newVersion, writeId };
|
|
8822
|
+
}
|
|
8823
|
+
async function withOccRetry2(operation, options) {
|
|
8824
|
+
const maxAttempts = options?.maxAttempts ?? 10;
|
|
8825
|
+
const isRetryable = options?.isRetryable;
|
|
8826
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
8827
|
+
try {
|
|
8828
|
+
return await operation();
|
|
8829
|
+
} catch (err) {
|
|
8830
|
+
const retryable = err instanceof OccWriteConflictError2 || (isRetryable?.(err) ?? false);
|
|
8831
|
+
if (retryable && attempt < maxAttempts) {
|
|
8832
|
+
const baseDelay = Math.min(10 * Math.pow(2, attempt - 1), 100);
|
|
8833
|
+
const jitter = Math.random() * baseDelay;
|
|
8834
|
+
await new Promise((resolve2) => setTimeout(resolve2, baseDelay + jitter));
|
|
8835
|
+
continue;
|
|
8836
|
+
}
|
|
8837
|
+
throw err;
|
|
8838
|
+
}
|
|
8839
|
+
}
|
|
8840
|
+
throw new Error("Unreachable");
|
|
8841
|
+
}
|
|
8842
|
+
async function withOccFileLock2(filePath, fn) {
|
|
8843
|
+
const dir = path27.dirname(filePath);
|
|
8844
|
+
await fs24.mkdir(dir, { recursive: true });
|
|
8845
|
+
const MAX_ATTEMPTS = 14;
|
|
8846
|
+
let release = null;
|
|
8847
|
+
for (let attempt = 1; release === null; attempt++) {
|
|
8848
|
+
try {
|
|
8849
|
+
release = await lockfile4.lock(filePath, {
|
|
8850
|
+
lockfilePath: `${filePath}.lock`,
|
|
8851
|
+
realpath: false,
|
|
8852
|
+
stale: 1e4,
|
|
8853
|
+
retries: 0,
|
|
8854
|
+
// A compromised lock (the lock dir vanished or refresh failed
|
|
8855
|
+
// mid-hold, e.g. the branch directory containing it was deleted)
|
|
8856
|
+
// must not crash the process, which is proper-lockfile's default.
|
|
8857
|
+
// Our critical sections are short and idempotent-on-conflict (OCC
|
|
8858
|
+
// verify inside); log and let the section finish.
|
|
8859
|
+
onCompromised: (err) => {
|
|
8860
|
+
log11.warn("lock", `Lock compromised mid-hold for ${filePath}`, {
|
|
8861
|
+
error: getErrorMessage(err)
|
|
8862
|
+
});
|
|
8863
|
+
}
|
|
8864
|
+
});
|
|
8865
|
+
} catch (err) {
|
|
8866
|
+
const code = isNodeError(err) ? err.code : void 0;
|
|
8867
|
+
const contended = code === "ELOCKED";
|
|
8868
|
+
if (!contended || attempt >= MAX_ATTEMPTS) {
|
|
8869
|
+
throw new OccWriteConflictError2(`Could not acquire file lock: ${getErrorMessage(err)}`);
|
|
8870
|
+
}
|
|
8871
|
+
const baseDelay = Math.min(50 * Math.pow(1.6, attempt - 1), 2e3);
|
|
8872
|
+
const jitter = Math.random() * baseDelay;
|
|
8873
|
+
await new Promise((resolve2) => setTimeout(resolve2, baseDelay + jitter));
|
|
8874
|
+
}
|
|
8875
|
+
}
|
|
8876
|
+
try {
|
|
8877
|
+
return await fn();
|
|
8878
|
+
} finally {
|
|
8879
|
+
await release().catch((err) => {
|
|
8880
|
+
if (isNodeError(err) && err.code === "ENOENT")
|
|
8881
|
+
return;
|
|
8882
|
+
log11.warn("lock", `Failed to release lock for ${filePath}`, { error: getErrorMessage(err) });
|
|
8883
|
+
});
|
|
8884
|
+
}
|
|
8885
|
+
}
|
|
8886
|
+
var log11, OccWriteConflictError2;
|
|
8887
|
+
var init_occ_json_write = __esm({
|
|
8888
|
+
"dist/utils/occ-json-write.js"() {
|
|
8889
|
+
"use strict";
|
|
8890
|
+
init_error();
|
|
8891
|
+
init_debug2();
|
|
8892
|
+
log11 = createDebugLogger3({ prefix: "OccJsonWrite" });
|
|
8893
|
+
OccWriteConflictError2 = class extends Error {
|
|
8894
|
+
constructor(message = "Concurrent modification detected") {
|
|
8895
|
+
super(message);
|
|
8896
|
+
this.name = "OccWriteConflictError";
|
|
8897
|
+
}
|
|
8898
|
+
};
|
|
8899
|
+
}
|
|
8900
|
+
});
|
|
8901
|
+
|
|
8902
|
+
// dist/branch-registry.js
|
|
8903
|
+
import fs25 from "node:fs/promises";
|
|
8904
|
+
import path28 from "node:path";
|
|
8905
|
+
var log12, REGISTRY_FILE2, REGISTRY_STALE_FILE2, REGISTRY_TEMP_FILE2, REGISTRY_VERSION2, RESOURCE4, GET_MISS_REFRESH_MIN_INTERVAL_MS2, BranchRegistry2;
|
|
8590
8906
|
var init_branch_registry = __esm({
|
|
8591
8907
|
"dist/branch-registry.js"() {
|
|
8592
8908
|
"use strict";
|
|
@@ -8594,7 +8910,7 @@ var init_branch_registry = __esm({
|
|
|
8594
8910
|
init_error();
|
|
8595
8911
|
init_debug2();
|
|
8596
8912
|
init_resource_generation();
|
|
8597
|
-
|
|
8913
|
+
log12 = createDebugLogger3({ prefix: "BranchRegistry" });
|
|
8598
8914
|
REGISTRY_FILE2 = "branches.json";
|
|
8599
8915
|
REGISTRY_STALE_FILE2 = "branches.stale.json";
|
|
8600
8916
|
REGISTRY_TEMP_FILE2 = "branches.tmp.json";
|
|
@@ -8605,10 +8921,10 @@ var init_branch_registry = __esm({
|
|
|
8605
8921
|
constructor(root) {
|
|
8606
8922
|
this.regenInFlight = null;
|
|
8607
8923
|
this.lastForcedRefreshMs = 0;
|
|
8608
|
-
this.root =
|
|
8609
|
-
this.registryPath =
|
|
8610
|
-
this.stalePath =
|
|
8611
|
-
this.tempPath =
|
|
8924
|
+
this.root = path28.resolve(root);
|
|
8925
|
+
this.registryPath = path28.join(this.root, REGISTRY_FILE2);
|
|
8926
|
+
this.stalePath = path28.join(this.root, REGISTRY_STALE_FILE2);
|
|
8927
|
+
this.tempPath = path28.join(this.root, REGISTRY_TEMP_FILE2);
|
|
8612
8928
|
}
|
|
8613
8929
|
/**
|
|
8614
8930
|
* Returns all branches. Uses the cached snapshot if its embedded generation
|
|
@@ -8617,7 +8933,7 @@ var init_branch_registry = __esm({
|
|
|
8617
8933
|
async list() {
|
|
8618
8934
|
let parsed;
|
|
8619
8935
|
try {
|
|
8620
|
-
const raw = await
|
|
8936
|
+
const raw = await fs25.readFile(this.registryPath, "utf8");
|
|
8621
8937
|
parsed = JSON.parse(raw);
|
|
8622
8938
|
} catch (err) {
|
|
8623
8939
|
if (isNotFoundError(err)) {
|
|
@@ -8680,7 +8996,7 @@ var init_branch_registry = __esm({
|
|
|
8680
8996
|
});
|
|
8681
8997
|
await this.regenerate();
|
|
8682
8998
|
} catch (err) {
|
|
8683
|
-
|
|
8999
|
+
log12.warn("invalidate", "Eager regeneration after invalidate() failed", {
|
|
8684
9000
|
error: getErrorMessage(err)
|
|
8685
9001
|
});
|
|
8686
9002
|
}
|
|
@@ -8706,7 +9022,7 @@ var init_branch_registry = __esm({
|
|
|
8706
9022
|
async performRegenerate() {
|
|
8707
9023
|
const read = await readResourceGeneration2(this.root, RESOURCE4);
|
|
8708
9024
|
const branches = await this.scanBranchDirectories();
|
|
8709
|
-
await
|
|
9025
|
+
await fs25.unlink(this.stalePath).catch(() => {
|
|
8710
9026
|
});
|
|
8711
9027
|
if (!read.ok) {
|
|
8712
9028
|
return branches;
|
|
@@ -8717,12 +9033,12 @@ var init_branch_registry = __esm({
|
|
|
8717
9033
|
generation: read.token
|
|
8718
9034
|
};
|
|
8719
9035
|
const uniqueTempPath = `${this.tempPath}.${Date.now()}.${Math.random().toString(36).slice(2)}`;
|
|
8720
|
-
await
|
|
8721
|
-
await
|
|
9036
|
+
await fs25.mkdir(this.root, { recursive: true });
|
|
9037
|
+
await fs25.writeFile(uniqueTempPath, JSON.stringify(snapshot, null, 2) + "\n", "utf8");
|
|
8722
9038
|
try {
|
|
8723
|
-
await
|
|
9039
|
+
await fs25.rename(uniqueTempPath, this.registryPath);
|
|
8724
9040
|
} catch (err) {
|
|
8725
|
-
await
|
|
9041
|
+
await fs25.unlink(uniqueTempPath).catch(() => {
|
|
8726
9042
|
});
|
|
8727
9043
|
throw err;
|
|
8728
9044
|
}
|
|
@@ -8736,13 +9052,19 @@ var init_branch_registry = __esm({
|
|
|
8736
9052
|
async scanBranchDirectories() {
|
|
8737
9053
|
const branches = [];
|
|
8738
9054
|
try {
|
|
8739
|
-
const entries = await
|
|
9055
|
+
const entries = await fs25.readdir(this.root, { withFileTypes: true });
|
|
8740
9056
|
for (const entry of entries) {
|
|
8741
9057
|
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
8742
9058
|
continue;
|
|
8743
9059
|
}
|
|
8744
|
-
const branchRoot =
|
|
8745
|
-
|
|
9060
|
+
const branchRoot = path28.join(this.root, entry.name);
|
|
9061
|
+
let meta;
|
|
9062
|
+
try {
|
|
9063
|
+
meta = await BranchMetadataFileManager2.loadOnly(branchRoot);
|
|
9064
|
+
} catch (err) {
|
|
9065
|
+
console.warn(`CanopyCMS: Skipping branch directory '${entry.name}' during registry scan: ${getErrorMessage(err)}`);
|
|
9066
|
+
continue;
|
|
9067
|
+
}
|
|
8746
9068
|
if (meta) {
|
|
8747
9069
|
branches.push({
|
|
8748
9070
|
branch: meta.branch,
|
|
@@ -8763,168 +9085,10 @@ var init_branch_registry = __esm({
|
|
|
8763
9085
|
}
|
|
8764
9086
|
});
|
|
8765
9087
|
|
|
8766
|
-
// dist/utils/async-mutex.js
|
|
8767
|
-
async function withLock2(key, fn) {
|
|
8768
|
-
const prev = locks2.get(key) ?? Promise.resolve();
|
|
8769
|
-
let resolve2;
|
|
8770
|
-
const next = new Promise((r) => {
|
|
8771
|
-
resolve2 = r;
|
|
8772
|
-
});
|
|
8773
|
-
locks2.set(key, next);
|
|
8774
|
-
await prev;
|
|
8775
|
-
try {
|
|
8776
|
-
return await fn();
|
|
8777
|
-
} finally {
|
|
8778
|
-
resolve2();
|
|
8779
|
-
if (locks2.get(key) === next)
|
|
8780
|
-
locks2.delete(key);
|
|
8781
|
-
}
|
|
8782
|
-
}
|
|
8783
|
-
var locks2;
|
|
8784
|
-
var init_async_mutex = __esm({
|
|
8785
|
-
"dist/utils/async-mutex.js"() {
|
|
8786
|
-
"use strict";
|
|
8787
|
-
locks2 = /* @__PURE__ */ new Map();
|
|
8788
|
-
}
|
|
8789
|
-
});
|
|
8790
|
-
|
|
8791
|
-
// dist/utils/occ-json-write.js
|
|
8792
|
-
import fs25 from "node:fs/promises";
|
|
8793
|
-
import path28 from "node:path";
|
|
8794
|
-
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
8795
|
-
import lockfile4 from "proper-lockfile";
|
|
8796
|
-
async function writeOccJsonFile2(filePath, payload, options) {
|
|
8797
|
-
const { expectedVersion, settleMs = 50, trailingNewline = false } = options;
|
|
8798
|
-
const newVersion = expectedVersion === null ? 1 : expectedVersion + 1;
|
|
8799
|
-
const writeId = randomUUID4();
|
|
8800
|
-
const data = { ...payload, version: newVersion, writeId };
|
|
8801
|
-
const content = JSON.stringify(data, null, 2) + (trailingNewline ? "\n" : "");
|
|
8802
|
-
const dir = path28.dirname(filePath);
|
|
8803
|
-
await fs25.mkdir(dir, { recursive: true });
|
|
8804
|
-
const tempPath = `${filePath}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
8805
|
-
await fs25.writeFile(tempPath, content, "utf-8");
|
|
8806
|
-
if (expectedVersion === null) {
|
|
8807
|
-
try {
|
|
8808
|
-
await fs25.link(tempPath, filePath);
|
|
8809
|
-
} catch (err) {
|
|
8810
|
-
await fs25.unlink(tempPath).catch(() => {
|
|
8811
|
-
});
|
|
8812
|
-
if (isFileExistsError(err)) {
|
|
8813
|
-
throw new OccWriteConflictError2();
|
|
8814
|
-
}
|
|
8815
|
-
throw err;
|
|
8816
|
-
}
|
|
8817
|
-
await fs25.unlink(tempPath).catch(() => {
|
|
8818
|
-
});
|
|
8819
|
-
return { version: newVersion, writeId };
|
|
8820
|
-
}
|
|
8821
|
-
try {
|
|
8822
|
-
let currentVersion = null;
|
|
8823
|
-
try {
|
|
8824
|
-
const current = JSON.parse(await fs25.readFile(filePath, "utf-8"));
|
|
8825
|
-
currentVersion = current.version ?? 0;
|
|
8826
|
-
} catch {
|
|
8827
|
-
currentVersion = null;
|
|
8828
|
-
}
|
|
8829
|
-
if (currentVersion !== expectedVersion) {
|
|
8830
|
-
throw new OccWriteConflictError2();
|
|
8831
|
-
}
|
|
8832
|
-
await fs25.rename(tempPath, filePath);
|
|
8833
|
-
if (settleMs > 0) {
|
|
8834
|
-
await new Promise((resolve2) => setTimeout(resolve2, settleMs));
|
|
8835
|
-
}
|
|
8836
|
-
const afterWrite = JSON.parse(await fs25.readFile(filePath, "utf-8"));
|
|
8837
|
-
if (afterWrite.writeId !== writeId) {
|
|
8838
|
-
throw new OccWriteConflictError2();
|
|
8839
|
-
}
|
|
8840
|
-
} catch (err) {
|
|
8841
|
-
await fs25.unlink(tempPath).catch(() => {
|
|
8842
|
-
});
|
|
8843
|
-
throw err;
|
|
8844
|
-
}
|
|
8845
|
-
return { version: newVersion, writeId };
|
|
8846
|
-
}
|
|
8847
|
-
async function withOccRetry2(operation, options) {
|
|
8848
|
-
const maxAttempts = options?.maxAttempts ?? 10;
|
|
8849
|
-
const isRetryable = options?.isRetryable;
|
|
8850
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
8851
|
-
try {
|
|
8852
|
-
return await operation();
|
|
8853
|
-
} catch (err) {
|
|
8854
|
-
const retryable = err instanceof OccWriteConflictError2 || (isRetryable?.(err) ?? false);
|
|
8855
|
-
if (retryable && attempt < maxAttempts) {
|
|
8856
|
-
const baseDelay = Math.min(10 * Math.pow(2, attempt - 1), 100);
|
|
8857
|
-
const jitter = Math.random() * baseDelay;
|
|
8858
|
-
await new Promise((resolve2) => setTimeout(resolve2, baseDelay + jitter));
|
|
8859
|
-
continue;
|
|
8860
|
-
}
|
|
8861
|
-
throw err;
|
|
8862
|
-
}
|
|
8863
|
-
}
|
|
8864
|
-
throw new Error("Unreachable");
|
|
8865
|
-
}
|
|
8866
|
-
async function withOccFileLock2(filePath, fn) {
|
|
8867
|
-
const dir = path28.dirname(filePath);
|
|
8868
|
-
await fs25.mkdir(dir, { recursive: true });
|
|
8869
|
-
const MAX_ATTEMPTS = 14;
|
|
8870
|
-
let release = null;
|
|
8871
|
-
for (let attempt = 1; release === null; attempt++) {
|
|
8872
|
-
try {
|
|
8873
|
-
release = await lockfile4.lock(filePath, {
|
|
8874
|
-
lockfilePath: `${filePath}.lock`,
|
|
8875
|
-
realpath: false,
|
|
8876
|
-
stale: 1e4,
|
|
8877
|
-
retries: 0,
|
|
8878
|
-
// A compromised lock (the lock dir vanished or refresh failed
|
|
8879
|
-
// mid-hold, e.g. the branch directory containing it was deleted)
|
|
8880
|
-
// must not crash the process, which is proper-lockfile's default.
|
|
8881
|
-
// Our critical sections are short and idempotent-on-conflict (OCC
|
|
8882
|
-
// verify inside); log and let the section finish.
|
|
8883
|
-
onCompromised: (err) => {
|
|
8884
|
-
log12.warn("lock", `Lock compromised mid-hold for ${filePath}`, {
|
|
8885
|
-
error: getErrorMessage(err)
|
|
8886
|
-
});
|
|
8887
|
-
}
|
|
8888
|
-
});
|
|
8889
|
-
} catch (err) {
|
|
8890
|
-
const code = isNodeError(err) ? err.code : void 0;
|
|
8891
|
-
const contended = code === "ELOCKED";
|
|
8892
|
-
if (!contended || attempt >= MAX_ATTEMPTS) {
|
|
8893
|
-
throw new OccWriteConflictError2(`Could not acquire file lock: ${getErrorMessage(err)}`);
|
|
8894
|
-
}
|
|
8895
|
-
const baseDelay = Math.min(50 * Math.pow(1.6, attempt - 1), 2e3);
|
|
8896
|
-
const jitter = Math.random() * baseDelay;
|
|
8897
|
-
await new Promise((resolve2) => setTimeout(resolve2, baseDelay + jitter));
|
|
8898
|
-
}
|
|
8899
|
-
}
|
|
8900
|
-
try {
|
|
8901
|
-
return await fn();
|
|
8902
|
-
} finally {
|
|
8903
|
-
await release().catch((err) => {
|
|
8904
|
-
log12.warn("lock", `Failed to release lock for ${filePath}`, { error: getErrorMessage(err) });
|
|
8905
|
-
});
|
|
8906
|
-
}
|
|
8907
|
-
}
|
|
8908
|
-
var log12, OccWriteConflictError2;
|
|
8909
|
-
var init_occ_json_write = __esm({
|
|
8910
|
-
"dist/utils/occ-json-write.js"() {
|
|
8911
|
-
"use strict";
|
|
8912
|
-
init_error();
|
|
8913
|
-
init_debug2();
|
|
8914
|
-
log12 = createDebugLogger3({ prefix: "OccJsonWrite" });
|
|
8915
|
-
OccWriteConflictError2 = class extends Error {
|
|
8916
|
-
constructor(message = "Concurrent modification detected") {
|
|
8917
|
-
super(message);
|
|
8918
|
-
this.name = "OccWriteConflictError";
|
|
8919
|
-
}
|
|
8920
|
-
};
|
|
8921
|
-
}
|
|
8922
|
-
});
|
|
8923
|
-
|
|
8924
9088
|
// dist/branch-metadata.js
|
|
8925
9089
|
import fs26 from "node:fs/promises";
|
|
8926
9090
|
import path29 from "node:path";
|
|
8927
|
-
var BRANCH_META_DIR2, BRANCH_META_FILE2, CURRENT_SCHEMA_VERSION2, BranchMetadataConflictError2, BranchMetadataFileManager2;
|
|
9091
|
+
var BRANCH_META_DIR2, BRANCH_META_FILE2, CURRENT_SCHEMA_VERSION2, BranchMetadataConflictError2, BranchMetadataCorruptError2, BranchMetadataFileManager2;
|
|
8928
9092
|
var init_branch_metadata = __esm({
|
|
8929
9093
|
"dist/branch-metadata.js"() {
|
|
8930
9094
|
"use strict";
|
|
@@ -8942,6 +9106,14 @@ var init_branch_metadata = __esm({
|
|
|
8942
9106
|
this.name = "BranchMetadataConflictError";
|
|
8943
9107
|
}
|
|
8944
9108
|
};
|
|
9109
|
+
BranchMetadataCorruptError2 = class extends Error {
|
|
9110
|
+
constructor(branchRoot, cause) {
|
|
9111
|
+
super(`Corrupt branch metadata in '${branchRoot}': ${cause}`);
|
|
9112
|
+
this.name = "BranchMetadataCorruptError";
|
|
9113
|
+
this.branchRoot = branchRoot;
|
|
9114
|
+
this.parseCause = cause;
|
|
9115
|
+
}
|
|
9116
|
+
};
|
|
8945
9117
|
BranchMetadataFileManager2 = class _BranchMetadataFileManager2 {
|
|
8946
9118
|
constructor(branchRoot, baseRoot, options) {
|
|
8947
9119
|
this.branchRoot = path29.resolve(branchRoot);
|
|
@@ -8954,7 +9126,8 @@ var init_branch_metadata = __esm({
|
|
|
8954
9126
|
* Use this for read-only access (e.g., in registry scanning or loadBranchContext).
|
|
8955
9127
|
*/
|
|
8956
9128
|
static async loadOnly(branchRoot) {
|
|
8957
|
-
const
|
|
9129
|
+
const resolvedRoot = path29.resolve(branchRoot);
|
|
9130
|
+
const filePath = path29.join(resolvedRoot, BRANCH_META_DIR2, BRANCH_META_FILE2);
|
|
8958
9131
|
try {
|
|
8959
9132
|
const raw = await fs26.readFile(filePath, "utf8");
|
|
8960
9133
|
return JSON.parse(raw);
|
|
@@ -8962,6 +9135,9 @@ var init_branch_metadata = __esm({
|
|
|
8962
9135
|
if (isNotFoundError(err)) {
|
|
8963
9136
|
return null;
|
|
8964
9137
|
}
|
|
9138
|
+
if (err instanceof SyntaxError) {
|
|
9139
|
+
throw new BranchMetadataCorruptError2(resolvedRoot, err.message);
|
|
9140
|
+
}
|
|
8965
9141
|
throw err;
|
|
8966
9142
|
}
|
|
8967
9143
|
}
|
|
@@ -9071,7 +9247,11 @@ var init_branch_metadata = __esm({
|
|
|
9071
9247
|
createdBy: existing?.branch.createdBy ?? incoming.branch?.createdBy ?? defaults.createdBy,
|
|
9072
9248
|
createdAt: existing?.branch.createdAt ?? defaults.createdAt,
|
|
9073
9249
|
// Fork point is recorded once at creation; later saves must not move it
|
|
9074
|
-
baseBranch: existing?.branch.baseBranch ?? incoming.branch?.baseBranch
|
|
9250
|
+
baseBranch: existing?.branch.baseBranch ?? incoming.branch?.baseBranch,
|
|
9251
|
+
// Always stamped fresh — the spreads above would otherwise let
|
|
9252
|
+
// the existing (creation-time) value win forever, freezing the
|
|
9253
|
+
// timestamp the editor's Branches panel sorts and displays by
|
|
9254
|
+
updatedAt: now
|
|
9075
9255
|
}
|
|
9076
9256
|
};
|
|
9077
9257
|
const written = await this.write(merged, version);
|
|
@@ -9256,15 +9436,31 @@ async function migrate(options) {
|
|
|
9256
9436
|
return { opCount: 0 };
|
|
9257
9437
|
}
|
|
9258
9438
|
}
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9439
|
+
const isBranchClone = Boolean(await BranchMetadataFileManager2.loadOnly(projectDir));
|
|
9440
|
+
const applyOps = async () => {
|
|
9441
|
+
for (const op of ops) {
|
|
9442
|
+
if (op.kind === "rename") {
|
|
9443
|
+
await fs27.rename(op.from, op.to);
|
|
9444
|
+
} else {
|
|
9445
|
+
await fs27.writeFile(op.filePath, op.content, "utf-8");
|
|
9446
|
+
}
|
|
9264
9447
|
}
|
|
9448
|
+
};
|
|
9449
|
+
if (isBranchClone) {
|
|
9450
|
+
const lockPath = path30.join(projectDir, ".canopy-meta", "schema");
|
|
9451
|
+
try {
|
|
9452
|
+
await withLock2(lockPath, () => withOccFileLock2(lockPath, applyOps));
|
|
9453
|
+
} catch (err) {
|
|
9454
|
+
if (err instanceof OccWriteConflictError2) {
|
|
9455
|
+
throw new MigrateError("Another process is modifying the schema, try again.");
|
|
9456
|
+
}
|
|
9457
|
+
throw err;
|
|
9458
|
+
}
|
|
9459
|
+
} else {
|
|
9460
|
+
await applyOps();
|
|
9265
9461
|
}
|
|
9266
9462
|
p3.log.success(`Applied ${ops.length} operation(s).`);
|
|
9267
|
-
if (
|
|
9463
|
+
if (isBranchClone) {
|
|
9268
9464
|
await invalidateBranchContentCaches2(projectDir);
|
|
9269
9465
|
}
|
|
9270
9466
|
try {
|
|
@@ -9288,6 +9484,8 @@ var init_migrate = __esm({
|
|
|
9288
9484
|
init_fs();
|
|
9289
9485
|
init_schema();
|
|
9290
9486
|
init_error();
|
|
9487
|
+
init_async_mutex();
|
|
9488
|
+
init_occ_json_write();
|
|
9291
9489
|
init_branch_metadata();
|
|
9292
9490
|
init_content_index_generation();
|
|
9293
9491
|
MIGRATE_FORMATS = ["md", "mdx", "json", "yaml"];
|