canopycms 0.0.67 → 0.0.68-int.93
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/handler.js +8 -0
- package/dist/api/admin-branch-health.js +12 -7
- package/dist/api/admin.d.ts +8 -8
- package/dist/api/branch-status.js +6 -2
- package/dist/api/client.d.ts +12 -0
- package/dist/api/client.js +35 -11
- package/dist/api/content.d.ts +6 -5
- package/dist/api/content.js +38 -39
- package/dist/api/entries.js +6 -8
- package/dist/api/github-sync.d.ts +10 -1
- package/dist/api/github-sync.js +20 -10
- package/dist/api/reference-options.js +1 -1
- package/dist/api/request-url.d.ts +8 -0
- package/dist/api/request-url.js +15 -0
- package/dist/api/resolve-references.js +2 -2
- package/dist/api/settings-helpers.d.ts +1 -1
- package/dist/api/settings-helpers.js +1 -4
- package/dist/authorization/content.d.ts +5 -4
- package/dist/authorization/content.js +5 -5
- package/dist/authorization/path.d.ts +11 -6
- package/dist/authorization/path.js +11 -6
- package/dist/authorization/types.d.ts +2 -2
- package/dist/branch-health.js +3 -3
- package/dist/branch-schema-cache.d.ts +5 -3
- package/dist/branch-schema-cache.js +29 -11
- package/dist/branch-workspace.js +2 -2
- package/dist/cli/cli.js +285 -168
- package/dist/cli/generate-ai-content.js +190 -109
- package/dist/cli/github-app-manifest.d.ts +4 -3
- package/dist/cli/github-app-manifest.js +4 -3
- package/dist/cli/init.js +15 -10
- package/dist/cli/sync.js +15 -13
- package/dist/config/schemas/config.d.ts +6 -3
- package/dist/config/schemas/config.js +3 -1
- package/dist/config/schemas/field.js +1 -0
- package/dist/config/types.d.ts +11 -2
- package/dist/config/validation.d.ts +6 -0
- package/dist/config/validation.js +37 -0
- package/dist/content-listing.d.ts +2 -2
- package/dist/content-listing.js +3 -3
- package/dist/content-reader.js +4 -4
- package/dist/content-store.d.ts +2 -0
- package/dist/content-store.js +2 -1
- package/dist/content-tree.js +1 -1
- package/dist/context.d.ts +16 -7
- package/dist/context.js +116 -67
- package/dist/editor/Editor.js +4 -4
- package/dist/editor/FormRenderer.js +20 -5
- package/dist/editor/admin/SystemHealthPanel.js +37 -6
- package/dist/editor/components/NoEditPermissionNotice.d.ts +10 -0
- package/dist/editor/components/NoEditPermissionNotice.js +17 -0
- package/dist/editor/hooks/useBranchesData.js +3 -1
- package/dist/editor/hooks/useDraftManager.d.ts +1 -1
- package/dist/editor/hooks/useDraftManager.js +2 -1
- package/dist/editor/hooks/useEntriesData.js +2 -2
- package/dist/editor/hooks/useEntryManager.js +17 -11
- package/dist/entry-schema-registry.js +2 -1
- package/dist/git-manager.d.ts +20 -0
- package/dist/git-manager.js +50 -4
- package/dist/github-service.d.ts +8 -0
- package/dist/github-service.js +5 -1
- package/dist/http/handler.js +24 -9
- package/dist/http/index.d.ts +2 -0
- package/dist/http/index.js +2 -0
- package/dist/http/router.d.ts +2 -0
- package/dist/http/router.js +2 -1
- package/dist/http/worker-not-ready.d.ts +9 -0
- package/dist/http/worker-not-ready.js +17 -0
- package/dist/operating-mode/client-unsafe-strategy.js +0 -6
- package/dist/operating-mode/types.d.ts +0 -3
- package/dist/paths/index.d.ts +1 -1
- package/dist/paths/index.js +1 -1
- package/dist/paths/normalize.d.ts +7 -0
- package/dist/paths/normalize.js +9 -0
- package/dist/reference-resolver.d.ts +3 -3
- package/dist/reference-resolver.js +3 -2
- package/dist/resolve-canopy-user.js +2 -1
- package/dist/services.d.ts +12 -5
- package/dist/services.js +56 -56
- package/dist/settings-workspace.js +36 -0
- package/dist/static/seo.d.ts +2 -14
- package/dist/static/seo.js +2 -25
- package/dist/submission-attribution.d.ts +73 -0
- package/dist/submission-attribution.js +221 -0
- package/dist/sync-core.js +7 -8
- package/dist/types.d.ts +23 -2
- package/dist/utils/content-write-lock.d.ts +3 -7
- package/dist/utils/content-write-lock.js +6 -4
- package/dist/utils/debug.d.ts +8 -0
- package/dist/utils/debug.js +10 -2
- package/dist/utils/git.d.ts +28 -0
- package/dist/utils/git.js +38 -0
- package/dist/utils/provisioning-lock.d.ts +15 -5
- package/dist/utils/provisioning-lock.js +31 -11
- package/dist/utils/request-timing.d.ts +25 -0
- package/dist/utils/request-timing.js +101 -0
- package/dist/utils/url-prefix.d.ts +15 -0
- package/dist/utils/url-prefix.js +26 -0
- package/dist/worker/canopy-state.d.ts +45 -0
- package/dist/worker/canopy-state.js +75 -0
- package/dist/worker/git-sync.d.ts +4 -2
- package/dist/worker/git-sync.js +111 -24
- package/dist/worker/provisioned-workspace.d.ts +35 -0
- package/dist/worker/provisioned-workspace.js +50 -0
- package/dist/worker/rebase.js +75 -12
- package/dist/worker/task-runner.js +39 -4
- package/package.json +1 -1
|
@@ -89,6 +89,9 @@ function createLogicalPath(...segments) {
|
|
|
89
89
|
}
|
|
90
90
|
return normalized;
|
|
91
91
|
}
|
|
92
|
+
function entryLogicalPath(collectionLogicalPath, slug) {
|
|
93
|
+
return collectionLogicalPath ? `${collectionLogicalPath}/${slug}` : slug;
|
|
94
|
+
}
|
|
92
95
|
function trimSlashes(path22) {
|
|
93
96
|
let start = 0;
|
|
94
97
|
let end = path22.length;
|
|
@@ -310,7 +313,8 @@ var init_field = __esm({
|
|
|
310
313
|
});
|
|
311
314
|
objectFieldSchema = fieldBaseSchema.extend({
|
|
312
315
|
type: z2.literal("object"),
|
|
313
|
-
fields: z2.array(z2.lazy(() => fieldHolder[0])).min(1)
|
|
316
|
+
fields: z2.array(z2.lazy(() => fieldHolder[0])).min(1),
|
|
317
|
+
itemTitleField: z2.string().min(1).optional()
|
|
314
318
|
});
|
|
315
319
|
inlineGroupFieldSchema = z2.object({
|
|
316
320
|
type: z2.literal("group"),
|
|
@@ -501,6 +505,9 @@ var init_config = __esm({
|
|
|
501
505
|
defaultRemoteUrl: defaultRemoteUrlSchema.optional(),
|
|
502
506
|
gitBotAuthorName: gitBotAuthorNameSchema,
|
|
503
507
|
gitBotAuthorEmail: gitBotAuthorEmailSchema,
|
|
508
|
+
// Unset reads as on for Edited-by and off for Co-authored-by (services.ts submitBranch).
|
|
509
|
+
gitEditedByTrailers: z6.boolean().optional(),
|
|
510
|
+
gitCoAuthoredByTrailers: z6.boolean().optional(),
|
|
504
511
|
githubTokenEnvVar: githubTokenEnvVarSchema.optional(),
|
|
505
512
|
// Required by design (follow-up to SEC-C1): a prod deploy that omits `mode` must fail
|
|
506
513
|
// validation loudly rather than silently running header-trusting dev auth semantics.
|
|
@@ -512,7 +519,6 @@ var init_config = __esm({
|
|
|
512
519
|
// Default false/unset — the standard AWS Lambda+worker topology must leave this unset.
|
|
513
520
|
allowNetworkRemoteInProd: z6.boolean().optional(),
|
|
514
521
|
settingsBranch: z6.string().optional(),
|
|
515
|
-
autoCreateSettingsPR: z6.boolean().optional(),
|
|
516
522
|
// `.optional()`, NOT bare `deploymentNameSchema`: its own `.default('prod')` would make
|
|
517
523
|
// `parse(undefined)` resolve to 'prod' instead of staying `undefined`, collapsing the
|
|
518
524
|
// env > config > modeDefault precedence chain `resolveDeploymentName`
|
|
@@ -835,9 +841,6 @@ var init_client_unsafe_strategy = __esm({
|
|
|
835
841
|
throw new Error("gitBotAuthorName and gitBotAuthorEmail are required in prod mode");
|
|
836
842
|
}
|
|
837
843
|
}
|
|
838
|
-
shouldCreateSettingsPR(config) {
|
|
839
|
-
return config.autoCreateSettingsPR ?? true;
|
|
840
|
-
}
|
|
841
844
|
};
|
|
842
845
|
DevStrategy = class extends DevClientSafeStrategy {
|
|
843
846
|
getWorkspaceRoot(sourceRoot) {
|
|
@@ -884,9 +887,6 @@ var init_client_unsafe_strategy = __esm({
|
|
|
884
887
|
}
|
|
885
888
|
validateConfig(_config) {
|
|
886
889
|
}
|
|
887
|
-
shouldCreateSettingsPR(_config) {
|
|
888
|
-
return false;
|
|
889
|
-
}
|
|
890
890
|
};
|
|
891
891
|
strategyCache = /* @__PURE__ */ new Map();
|
|
892
892
|
}
|
|
@@ -1193,12 +1193,15 @@ import fs2 from "node:fs/promises";
|
|
|
1193
1193
|
import path2 from "node:path";
|
|
1194
1194
|
import lockfile from "proper-lockfile";
|
|
1195
1195
|
init_logger();
|
|
1196
|
-
|
|
1196
|
+
var PROVISIONING_LOCK_STALE_MS = 9e4;
|
|
1197
|
+
function provisioningLockOptions(lockPath, retries, onCompromised, staleMs) {
|
|
1197
1198
|
return {
|
|
1198
1199
|
lockfilePath: lockPath,
|
|
1199
1200
|
realpath: false,
|
|
1200
1201
|
retries,
|
|
1201
|
-
stale:
|
|
1202
|
+
stale: staleMs,
|
|
1203
|
+
// Fixed, not stale/2: every holder refreshes at 15s whatever threshold it judges others by.
|
|
1204
|
+
update: 15e3,
|
|
1202
1205
|
// proper-lockfile invokes this from inside its refresh timer, so ANY throw escaping here is
|
|
1203
1206
|
// an uncaught exception that kills the process. Call sites are told not to throw (see
|
|
1204
1207
|
// OnLockCompromised); this makes it structural rather than a convention, and also covers the
|
|
@@ -1234,16 +1237,19 @@ function releaseIgnoringAlreadyReleased(release, lockPath) {
|
|
|
1234
1237
|
}
|
|
1235
1238
|
};
|
|
1236
1239
|
}
|
|
1240
|
+
function branchProvisioningLockName(dirName) {
|
|
1241
|
+
return `.${dirName}.init.lock`;
|
|
1242
|
+
}
|
|
1237
1243
|
async function acquireProvisioningLock(lockTargetDir2, lockName, onCompromised) {
|
|
1238
1244
|
await fs2.mkdir(lockTargetDir2, { recursive: true });
|
|
1239
1245
|
const lockPath = path2.join(lockTargetDir2, lockName);
|
|
1240
|
-
const release = await lockfile.lock(lockPath, provisioningLockOptions(lockPath, { retries: 600, factor: 1, minTimeout: 300, maxTimeout: 800, randomize: true }, onCompromised));
|
|
1246
|
+
const release = await lockfile.lock(lockPath, provisioningLockOptions(lockPath, { retries: 600, factor: 1, minTimeout: 300, maxTimeout: 800, randomize: true }, onCompromised, PROVISIONING_LOCK_STALE_MS));
|
|
1241
1247
|
return releaseIgnoringAlreadyReleased(release, lockPath);
|
|
1242
1248
|
}
|
|
1243
|
-
async function tryAcquireProvisioningLock(lockTargetDir2, lockName, onCompromised) {
|
|
1249
|
+
async function tryAcquireProvisioningLock(lockTargetDir2, lockName, onCompromised, staleMs = PROVISIONING_LOCK_STALE_MS) {
|
|
1244
1250
|
await fs2.mkdir(lockTargetDir2, { recursive: true });
|
|
1245
1251
|
const lockPath = path2.join(lockTargetDir2, lockName);
|
|
1246
|
-
const release = await lockfile.lock(lockPath, provisioningLockOptions(lockPath, 0, onCompromised));
|
|
1252
|
+
const release = await lockfile.lock(lockPath, provisioningLockOptions(lockPath, 0, onCompromised, staleMs));
|
|
1247
1253
|
return releaseIgnoringAlreadyReleased(release, lockPath);
|
|
1248
1254
|
}
|
|
1249
1255
|
|
|
@@ -1263,8 +1269,9 @@ function lockTargetDir(branchRoot) {
|
|
|
1263
1269
|
var sleep = (ms) => new Promise((resolve2) => {
|
|
1264
1270
|
setTimeout(resolve2, ms);
|
|
1265
1271
|
});
|
|
1272
|
+
var CONTENT_WRITE_LOCK_STALE_MS = 3e4;
|
|
1266
1273
|
function tryAcquireContentWriteLock(branchRoot, onCompromised) {
|
|
1267
|
-
return tryAcquireProvisioningLock(lockTargetDir(branchRoot), CONTENT_WRITE_LOCK_NAME, onCompromised);
|
|
1274
|
+
return tryAcquireProvisioningLock(lockTargetDir(branchRoot), CONTENT_WRITE_LOCK_NAME, onCompromised, CONTENT_WRITE_LOCK_STALE_MS);
|
|
1268
1275
|
}
|
|
1269
1276
|
async function acquireContentWriteLock(branchRoot, waitMs = DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, onCompromised) {
|
|
1270
1277
|
const startedAt = Date.now();
|
|
@@ -2048,6 +2055,10 @@ var DebugLogger = class {
|
|
|
2048
2055
|
throw new Error(errorMsg);
|
|
2049
2056
|
}
|
|
2050
2057
|
}
|
|
2058
|
+
/**
|
|
2059
|
+
* Keyed by label alone, so two overlapping timers with one label overwrite each other;
|
|
2060
|
+
* anything that can run concurrently uses {@link timed}.
|
|
2061
|
+
*/
|
|
2051
2062
|
time(label) {
|
|
2052
2063
|
this.timers.set(label, Date.now());
|
|
2053
2064
|
}
|
|
@@ -2062,12 +2073,16 @@ var DebugLogger = class {
|
|
|
2062
2073
|
this.debug(category, `${label} completed`, { durationMs: duration });
|
|
2063
2074
|
return duration;
|
|
2064
2075
|
}
|
|
2076
|
+
/**
|
|
2077
|
+
* Log `<label> completed {durationMs}` once `fn` settles. The start time is local to the
|
|
2078
|
+
* call, so concurrent spans with the same label on one logger never overwrite each other.
|
|
2079
|
+
*/
|
|
2065
2080
|
async timed(category, label, fn) {
|
|
2066
|
-
|
|
2081
|
+
const start = Date.now();
|
|
2067
2082
|
try {
|
|
2068
2083
|
return await fn();
|
|
2069
2084
|
} finally {
|
|
2070
|
-
this.
|
|
2085
|
+
this.debug(category, `${label} completed`, { durationMs: Date.now() - start });
|
|
2071
2086
|
}
|
|
2072
2087
|
}
|
|
2073
2088
|
};
|
|
@@ -2393,9 +2408,80 @@ var STATIC_DEPLOY_USER = Object.freeze({
|
|
|
2393
2408
|
name: "Static Deploy"
|
|
2394
2409
|
});
|
|
2395
2410
|
|
|
2411
|
+
// dist/utils/request-timing.js
|
|
2412
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2413
|
+
var storage = new AsyncLocalStorage();
|
|
2414
|
+
var log2 = createDebugLogger({ prefix: "CanopyCMS" });
|
|
2415
|
+
async function timeRequestPhase(phase, fn) {
|
|
2416
|
+
const parent = storage.getStore();
|
|
2417
|
+
if (!parent)
|
|
2418
|
+
return fn();
|
|
2419
|
+
const key = parent.path ? `${parent.path}>${phase}` : phase;
|
|
2420
|
+
let total = parent.phases.get(key);
|
|
2421
|
+
if (!total) {
|
|
2422
|
+
total = { count: 0, ms: 0 };
|
|
2423
|
+
parent.phases.set(key, total);
|
|
2424
|
+
}
|
|
2425
|
+
const start = performance.now();
|
|
2426
|
+
try {
|
|
2427
|
+
return await storage.run({ ...parent, path: key }, fn);
|
|
2428
|
+
} finally {
|
|
2429
|
+
total.count += 1;
|
|
2430
|
+
total.ms += performance.now() - start;
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
// dist/utils/git.js
|
|
2435
|
+
import { simpleGit } from "simple-git";
|
|
2436
|
+
var NETWORK_SCHEME_PATTERN = /^(https?|ssh|git):\/\//i;
|
|
2437
|
+
var TRANSPORT_HELPER_PATTERN = /^[a-z][a-z0-9+.-]*::/i;
|
|
2438
|
+
var LEADING_OPTION_PATTERN = /^-/;
|
|
2439
|
+
var WINDOWS_DRIVE_PATTERN = /^[a-zA-Z]:[\\/]/;
|
|
2440
|
+
var COLON_BEFORE_SLASH_PATTERN = /^[^/\\\s]+:/;
|
|
2441
|
+
function isNetworkRemoteUrl(url) {
|
|
2442
|
+
if (/^file:\/\//i.test(url))
|
|
2443
|
+
return false;
|
|
2444
|
+
if (NETWORK_SCHEME_PATTERN.test(url))
|
|
2445
|
+
return true;
|
|
2446
|
+
if (TRANSPORT_HELPER_PATTERN.test(url))
|
|
2447
|
+
return true;
|
|
2448
|
+
if (LEADING_OPTION_PATTERN.test(url))
|
|
2449
|
+
return true;
|
|
2450
|
+
if (WINDOWS_DRIVE_PATTERN.test(url))
|
|
2451
|
+
return false;
|
|
2452
|
+
return COLON_BEFORE_SLASH_PATTERN.test(url);
|
|
2453
|
+
}
|
|
2454
|
+
var MISSING_REMOTE_REF_REASONS = ["couldn't find remote ref", "Couldn't find remote ref"];
|
|
2455
|
+
function isMissingRemoteRefFailure(message) {
|
|
2456
|
+
return MISSING_REMOTE_REF_REASONS.some((reason) => message.includes(reason));
|
|
2457
|
+
}
|
|
2458
|
+
var CANOPY_META_DIR = ".canopy-meta";
|
|
2459
|
+
async function stageAllExceptCanopyState(git) {
|
|
2460
|
+
await git.add(["-A"]);
|
|
2461
|
+
await git.raw(["reset", "-q", "--", CANOPY_META_DIR]);
|
|
2462
|
+
}
|
|
2463
|
+
async function detectHeadBranch(repoRoot, fallback = "main") {
|
|
2464
|
+
try {
|
|
2465
|
+
const git = simpleGit({ baseDir: repoRoot });
|
|
2466
|
+
const head = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
|
|
2467
|
+
return head && head !== "HEAD" ? head : fallback;
|
|
2468
|
+
} catch {
|
|
2469
|
+
return fallback;
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
async function resolveBaseBranch(options) {
|
|
2473
|
+
if (options.defaultBaseBranch)
|
|
2474
|
+
return options.defaultBaseBranch;
|
|
2475
|
+
if (options.mode === "dev") {
|
|
2476
|
+
return detectHeadBranch(options.detectFrom ?? process.cwd());
|
|
2477
|
+
}
|
|
2478
|
+
return "main";
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2396
2481
|
// dist/branch-schema-cache.js
|
|
2397
2482
|
var SCHEMA_CACHE_VERSION = 3;
|
|
2398
2483
|
var MTIME_CHECK_DEBOUNCE_MS = 1e3;
|
|
2484
|
+
var SCHEMA_CACHE_FILE = "schema-cache.json";
|
|
2399
2485
|
var SCHEMA_GENERATION_RESOURCE = "schema";
|
|
2400
2486
|
async function isStaleByMtime(dir, cachedAt) {
|
|
2401
2487
|
let entries;
|
|
@@ -2418,6 +2504,11 @@ async function isStaleByMtime(dir, cachedAt) {
|
|
|
2418
2504
|
}
|
|
2419
2505
|
return false;
|
|
2420
2506
|
}
|
|
2507
|
+
async function schemaCacheDir(branchRoot) {
|
|
2508
|
+
const gitDir = path8.join(branchRoot, ".git");
|
|
2509
|
+
const isClone = await fs7.stat(gitDir).then((stat) => stat.isDirectory(), () => false);
|
|
2510
|
+
return isClone ? path8.join(gitDir, "canopycms") : path8.join(branchRoot, CANOPY_META_DIR);
|
|
2511
|
+
}
|
|
2421
2512
|
var BranchSchemaCache = class {
|
|
2422
2513
|
constructor(mode = "prod") {
|
|
2423
2514
|
this.lastMtimeCheck = /* @__PURE__ */ new Map();
|
|
@@ -2426,7 +2517,7 @@ var BranchSchemaCache = class {
|
|
|
2426
2517
|
/**
|
|
2427
2518
|
* Whether to skip the on-disk cache for this branchRoot.
|
|
2428
2519
|
*
|
|
2429
|
-
* Never write
|
|
2520
|
+
* Never write a schema cache at the project root, whichever entrypoint
|
|
2430
2521
|
* produced the cwd branchRoot. branchRoot equals process.cwd() only in the
|
|
2431
2522
|
* synthetic contexts static deployments and build phases use; a real branch
|
|
2432
2523
|
* root is always nested under the workspace.
|
|
@@ -2436,7 +2527,7 @@ var BranchSchemaCache = class {
|
|
|
2436
2527
|
}
|
|
2437
2528
|
/** Get schema for a branch, from the cache when fresh, else resolved fresh. */
|
|
2438
2529
|
async getSchema(branchRoot, entrySchemaRegistry, contentRootName = "content") {
|
|
2439
|
-
return this.loadFromCacheOrResolve(branchRoot, entrySchemaRegistry, contentRootName);
|
|
2530
|
+
return timeRequestPhase("schema", () => this.loadFromCacheOrResolve(branchRoot, entrySchemaRegistry, contentRootName));
|
|
2440
2531
|
}
|
|
2441
2532
|
/**
|
|
2442
2533
|
* Resolve the schema from disk. Protected rather than a direct resolveSchema
|
|
@@ -2450,8 +2541,7 @@ var BranchSchemaCache = class {
|
|
|
2450
2541
|
const contentRoot = path8.join(branchRoot, contentRootName);
|
|
2451
2542
|
const skipDiskCache = this.skipDiskCache(branchRoot);
|
|
2452
2543
|
if (!skipDiskCache) {
|
|
2453
|
-
const
|
|
2454
|
-
const cachePath = path8.join(cacheDir, "schema-cache.json");
|
|
2544
|
+
const cachePath = path8.join(await schemaCacheDir(branchRoot), SCHEMA_CACHE_FILE);
|
|
2455
2545
|
let cacheData = null;
|
|
2456
2546
|
try {
|
|
2457
2547
|
const cacheContent = await fs7.readFile(cachePath, "utf-8");
|
|
@@ -2493,7 +2583,7 @@ var BranchSchemaCache = class {
|
|
|
2493
2583
|
const { skipDiskCache } = options;
|
|
2494
2584
|
const contentRoot = path8.join(branchRoot, contentRootName);
|
|
2495
2585
|
const read = skipDiskCache ? null : await readResourceGeneration(branchRoot, SCHEMA_GENERATION_RESOURCE);
|
|
2496
|
-
const result = await this.resolveFresh(contentRoot, entrySchemaRegistry);
|
|
2586
|
+
const result = await timeRequestPhase("resolve", () => this.resolveFresh(contentRoot, entrySchemaRegistry));
|
|
2497
2587
|
if (!isValidSchema(result.schema)) {
|
|
2498
2588
|
throw new Error(`No schema found in ${contentRoot}. Create .collection.json files with references to field schemas defined in your entry schema registry.`);
|
|
2499
2589
|
}
|
|
@@ -2504,9 +2594,9 @@ var BranchSchemaCache = class {
|
|
|
2504
2594
|
}
|
|
2505
2595
|
const flatSchema = flattenSchema(result.schema, contentRootName);
|
|
2506
2596
|
if (!skipDiskCache) {
|
|
2507
|
-
const cacheDir =
|
|
2508
|
-
const cachePath = path8.join(cacheDir,
|
|
2509
|
-
await fs7.unlink(path8.join(
|
|
2597
|
+
const cacheDir = await schemaCacheDir(branchRoot);
|
|
2598
|
+
const cachePath = path8.join(cacheDir, SCHEMA_CACHE_FILE);
|
|
2599
|
+
await fs7.unlink(path8.join(branchRoot, CANOPY_META_DIR, "schema-cache.stale")).catch(() => {
|
|
2510
2600
|
});
|
|
2511
2601
|
if (read && read.ok) {
|
|
2512
2602
|
const newCache = {
|
|
@@ -3116,6 +3206,7 @@ var ContentStore = class {
|
|
|
3116
3206
|
return {
|
|
3117
3207
|
absolutePath: resolved,
|
|
3118
3208
|
relativePath: path11.relative(this.root, resolved),
|
|
3209
|
+
logicalPath: entryLogicalPath(schemaItem.logicalPath, safeSlug),
|
|
3119
3210
|
id,
|
|
3120
3211
|
entryTypeName: finalEntryTypeName,
|
|
3121
3212
|
existed
|
|
@@ -4353,7 +4444,7 @@ function yamlValue(value) {
|
|
|
4353
4444
|
}
|
|
4354
4445
|
|
|
4355
4446
|
// dist/entry-link-resolver.js
|
|
4356
|
-
var
|
|
4447
|
+
var log3 = createDebugLogger({ prefix: "EntryLinks" });
|
|
4357
4448
|
var BASE58_CHAR = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]";
|
|
4358
4449
|
var ENTRY_LINK_PATTERN = new RegExp(`entry:(${BASE58_CHAR}{12})(#[^\\s)>"']*)?`, "g");
|
|
4359
4450
|
function resolveEntryUrl(location, contentRoot) {
|
|
@@ -4367,7 +4458,7 @@ function resolveEntryLinksInText(text, idIndex, contentRoot, customResolver) {
|
|
|
4367
4458
|
return part.text.replace(ENTRY_LINK_PATTERN, (_match, id, anchor) => {
|
|
4368
4459
|
const location = idIndex.findById(id);
|
|
4369
4460
|
if (!location || location.type !== "entry" || !location.collection || !location.slug) {
|
|
4370
|
-
|
|
4461
|
+
log3.warn("resolve", `Entry link target not found: entry:${id}`);
|
|
4371
4462
|
return anchor ?? "#";
|
|
4372
4463
|
}
|
|
4373
4464
|
let url;
|
|
@@ -4765,7 +4856,7 @@ async function readBranchMetadataFile(branchRoot) {
|
|
|
4765
4856
|
|
|
4766
4857
|
// dist/branch-registry.js
|
|
4767
4858
|
init_logger();
|
|
4768
|
-
var
|
|
4859
|
+
var log4 = createDebugLogger({ prefix: "BranchRegistry" });
|
|
4769
4860
|
var REGISTRY_FILE = "branches.json";
|
|
4770
4861
|
var REGISTRY_STALE_FILE = "branches.stale.json";
|
|
4771
4862
|
var REGISTRY_TEMP_FILE = "branches.tmp.json";
|
|
@@ -4845,7 +4936,7 @@ var BranchRegistry = class {
|
|
|
4845
4936
|
});
|
|
4846
4937
|
await this.regenerate();
|
|
4847
4938
|
} catch (err) {
|
|
4848
|
-
|
|
4939
|
+
log4.warn("invalidate", "Eager regeneration after invalidate() failed", {
|
|
4849
4940
|
error: getErrorMessage(err)
|
|
4850
4941
|
});
|
|
4851
4942
|
}
|
|
@@ -4936,7 +5027,7 @@ import fs14 from "node:fs/promises";
|
|
|
4936
5027
|
import path15 from "node:path";
|
|
4937
5028
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
4938
5029
|
import lockfile2 from "proper-lockfile";
|
|
4939
|
-
var
|
|
5030
|
+
var log5 = createDebugLogger({ prefix: "OccJsonWrite" });
|
|
4940
5031
|
var OccWriteConflictError = class extends Error {
|
|
4941
5032
|
constructor(message = "Concurrent modification detected") {
|
|
4942
5033
|
super(message);
|
|
@@ -5031,7 +5122,7 @@ async function withOccFileLock(filePath, fn) {
|
|
|
5031
5122
|
// Our critical sections are short and idempotent-on-conflict (OCC
|
|
5032
5123
|
// verify inside); log and let the section finish.
|
|
5033
5124
|
onCompromised: (err) => {
|
|
5034
|
-
|
|
5125
|
+
log5.warn("lock", `Lock compromised mid-hold for ${filePath}`, {
|
|
5035
5126
|
error: getErrorMessage(err)
|
|
5036
5127
|
});
|
|
5037
5128
|
}
|
|
@@ -5053,7 +5144,7 @@ async function withOccFileLock(filePath, fn) {
|
|
|
5053
5144
|
await release().catch((err) => {
|
|
5054
5145
|
if (isNodeError(err) && err.code === "ENOENT")
|
|
5055
5146
|
return;
|
|
5056
|
-
|
|
5147
|
+
log5.warn("lock", `Failed to release lock for ${filePath}`, { error: getErrorMessage(err) });
|
|
5057
5148
|
});
|
|
5058
5149
|
}
|
|
5059
5150
|
}
|
|
@@ -5229,51 +5320,7 @@ init_operating_mode();
|
|
|
5229
5320
|
import fs16 from "node:fs/promises";
|
|
5230
5321
|
import path17 from "node:path";
|
|
5231
5322
|
import { simpleGit as simpleGit2 } from "simple-git";
|
|
5232
|
-
|
|
5233
|
-
// dist/utils/git.js
|
|
5234
|
-
import { simpleGit } from "simple-git";
|
|
5235
|
-
var NETWORK_SCHEME_PATTERN = /^(https?|ssh|git):\/\//i;
|
|
5236
|
-
var TRANSPORT_HELPER_PATTERN = /^[a-z][a-z0-9+.-]*::/i;
|
|
5237
|
-
var LEADING_OPTION_PATTERN = /^-/;
|
|
5238
|
-
var WINDOWS_DRIVE_PATTERN = /^[a-zA-Z]:[\\/]/;
|
|
5239
|
-
var COLON_BEFORE_SLASH_PATTERN = /^[^/\\\s]+:/;
|
|
5240
|
-
function isNetworkRemoteUrl(url) {
|
|
5241
|
-
if (/^file:\/\//i.test(url))
|
|
5242
|
-
return false;
|
|
5243
|
-
if (NETWORK_SCHEME_PATTERN.test(url))
|
|
5244
|
-
return true;
|
|
5245
|
-
if (TRANSPORT_HELPER_PATTERN.test(url))
|
|
5246
|
-
return true;
|
|
5247
|
-
if (LEADING_OPTION_PATTERN.test(url))
|
|
5248
|
-
return true;
|
|
5249
|
-
if (WINDOWS_DRIVE_PATTERN.test(url))
|
|
5250
|
-
return false;
|
|
5251
|
-
return COLON_BEFORE_SLASH_PATTERN.test(url);
|
|
5252
|
-
}
|
|
5253
|
-
var MISSING_REMOTE_REF_REASONS = ["couldn't find remote ref", "Couldn't find remote ref"];
|
|
5254
|
-
function isMissingRemoteRefFailure(message) {
|
|
5255
|
-
return MISSING_REMOTE_REF_REASONS.some((reason) => message.includes(reason));
|
|
5256
|
-
}
|
|
5257
|
-
async function detectHeadBranch(repoRoot, fallback = "main") {
|
|
5258
|
-
try {
|
|
5259
|
-
const git = simpleGit({ baseDir: repoRoot });
|
|
5260
|
-
const head = (await git.revparse(["--abbrev-ref", "HEAD"])).trim();
|
|
5261
|
-
return head && head !== "HEAD" ? head : fallback;
|
|
5262
|
-
} catch {
|
|
5263
|
-
return fallback;
|
|
5264
|
-
}
|
|
5265
|
-
}
|
|
5266
|
-
async function resolveBaseBranch(options) {
|
|
5267
|
-
if (options.defaultBaseBranch)
|
|
5268
|
-
return options.defaultBaseBranch;
|
|
5269
|
-
if (options.mode === "dev") {
|
|
5270
|
-
return detectHeadBranch(options.detectFrom ?? process.cwd());
|
|
5271
|
-
}
|
|
5272
|
-
return "main";
|
|
5273
|
-
}
|
|
5274
|
-
|
|
5275
|
-
// dist/git-manager.js
|
|
5276
|
-
var log5 = createDebugLogger({ prefix: "GitManager" });
|
|
5323
|
+
var log6 = createDebugLogger({ prefix: "GitManager" });
|
|
5277
5324
|
var GIT_ENV_PASSTHROUGH = /^(PATH|HOME|USER|LANG|LC_[A-Z]+|TZ|TMPDIR|GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL|DATE)|GIT_TERMINAL_PROMPT|GIT_TRACE[0-9A-Z_]*)$/;
|
|
5278
5325
|
var FORCE_C_LOCALE = { LC_ALL: "C", LANG: "C" };
|
|
5279
5326
|
function gitChildEnv(overrides) {
|
|
@@ -5298,13 +5345,13 @@ async function ensureGitExcludePattern(repoPath, pattern) {
|
|
|
5298
5345
|
}
|
|
5299
5346
|
const lines = content.split("\n");
|
|
5300
5347
|
if (lines.some((line) => line.trim() === pattern)) {
|
|
5301
|
-
|
|
5348
|
+
log6.debug("git", "Pattern already in .git/info/exclude", { pattern });
|
|
5302
5349
|
return;
|
|
5303
5350
|
}
|
|
5304
5351
|
const needsLeadingNewline = content.length > 0 && !content.endsWith("\n");
|
|
5305
5352
|
const newContent = content + (needsLeadingNewline ? "\n" : "") + pattern + "\n";
|
|
5306
5353
|
await fs16.writeFile(excludePath, newContent, "utf-8");
|
|
5307
|
-
|
|
5354
|
+
log6.debug("git", "Added pattern to .git/info/exclude", { pattern });
|
|
5308
5355
|
}
|
|
5309
5356
|
var GitConflictError = class extends Error {
|
|
5310
5357
|
constructor(conflictedFiles) {
|
|
@@ -5322,6 +5369,13 @@ var GitRemoteRefMissingError = class extends Error {
|
|
|
5322
5369
|
this.name = "GitRemoteRefMissingError";
|
|
5323
5370
|
}
|
|
5324
5371
|
};
|
|
5372
|
+
var RemoteNotReadyError = class extends Error {
|
|
5373
|
+
constructor(expectedRemotePath) {
|
|
5374
|
+
super(`CanopyCMS: no git remote is available yet. defaultRemoteUrl (or CANOPYCMS_REMOTE_URL) is not set and the CMS worker has not created ${expectedRemotePath}; the worker may still be starting.`);
|
|
5375
|
+
this.expectedRemotePath = expectedRemotePath;
|
|
5376
|
+
this.name = "RemoteNotReadyError";
|
|
5377
|
+
}
|
|
5378
|
+
};
|
|
5325
5379
|
var GitManager = class _GitManager {
|
|
5326
5380
|
constructor(options, gitOptions) {
|
|
5327
5381
|
this.repoPath = path17.resolve(options.repoPath);
|
|
@@ -5332,14 +5386,14 @@ var GitManager = class _GitManager {
|
|
|
5332
5386
|
this.git.env(gitChildEnv({ GIT_CEILING_DIRECTORIES: path17.dirname(this.repoPath) }));
|
|
5333
5387
|
}
|
|
5334
5388
|
static async cloneRepo(remoteUrl, targetPath, baseBranch = "main") {
|
|
5335
|
-
|
|
5389
|
+
log6.debug("git", "Cloning repository", {
|
|
5336
5390
|
remoteUrl,
|
|
5337
5391
|
targetPath,
|
|
5338
5392
|
baseBranch
|
|
5339
5393
|
});
|
|
5340
5394
|
const git = simpleGit2();
|
|
5341
5395
|
await git.clone(remoteUrl, targetPath, ["--branch", baseBranch, "--single-branch"]);
|
|
5342
|
-
|
|
5396
|
+
log6.debug("git", "Clone complete");
|
|
5343
5397
|
}
|
|
5344
5398
|
/**
|
|
5345
5399
|
* Initializes a local bare git repository to simulate a remote for dev mode,
|
|
@@ -5353,15 +5407,15 @@ var GitManager = class _GitManager {
|
|
|
5353
5407
|
static async ensureLocalSimulatedRemote(options) {
|
|
5354
5408
|
const existingLock = remoteInitLocks.get(options.remotePath);
|
|
5355
5409
|
if (existingLock) {
|
|
5356
|
-
|
|
5410
|
+
log6.debug("git", "Waiting for existing remote initialization", {
|
|
5357
5411
|
remotePath: options.remotePath
|
|
5358
5412
|
});
|
|
5359
5413
|
await existingLock;
|
|
5360
5414
|
}
|
|
5361
|
-
const lockPromise =
|
|
5415
|
+
const lockPromise = log6.timed("git", "ensureLocalSimulatedRemote", async () => {
|
|
5362
5416
|
let releaseLock;
|
|
5363
5417
|
try {
|
|
5364
|
-
|
|
5418
|
+
log6.debug("git", "Initializing local simulated remote", {
|
|
5365
5419
|
remotePath: options.remotePath,
|
|
5366
5420
|
baseBranch: options.baseBranch
|
|
5367
5421
|
});
|
|
@@ -5376,7 +5430,7 @@ var GitManager = class _GitManager {
|
|
|
5376
5430
|
throw err;
|
|
5377
5431
|
}
|
|
5378
5432
|
if (remoteExists && await _GitManager.bareRemoteHasBranch(options.remotePath, options.baseBranch)) {
|
|
5379
|
-
|
|
5433
|
+
log6.debug("git", "Remote already has base branch, skipping");
|
|
5380
5434
|
return;
|
|
5381
5435
|
}
|
|
5382
5436
|
let gitRoot = options.sourcePath;
|
|
@@ -5395,8 +5449,8 @@ var GitManager = class _GitManager {
|
|
|
5395
5449
|
}
|
|
5396
5450
|
let hasCommits = false;
|
|
5397
5451
|
try {
|
|
5398
|
-
const
|
|
5399
|
-
hasCommits =
|
|
5452
|
+
const log9 = await sourceGit.log(["-1"]);
|
|
5453
|
+
hasCommits = log9.total > 0;
|
|
5400
5454
|
} catch {
|
|
5401
5455
|
hasCommits = false;
|
|
5402
5456
|
}
|
|
@@ -5408,12 +5462,12 @@ var GitManager = class _GitManager {
|
|
|
5408
5462
|
throw new Error(`Cannot initialize local simulated remote: base branch '${options.baseBranch}' does not exist locally. Please checkout '${options.baseBranch}' first or provide an explicit remoteUrl.`);
|
|
5409
5463
|
}
|
|
5410
5464
|
if (remoteExists) {
|
|
5411
|
-
|
|
5465
|
+
log6.debug("git", "Existing remote is missing base branch \u2014 pushing it from source", {
|
|
5412
5466
|
remotePath: options.remotePath,
|
|
5413
5467
|
baseBranch: options.baseBranch
|
|
5414
5468
|
});
|
|
5415
5469
|
} else {
|
|
5416
|
-
|
|
5470
|
+
log6.debug("git", "Creating bare remote repository");
|
|
5417
5471
|
await simpleGit2().raw([
|
|
5418
5472
|
"init",
|
|
5419
5473
|
"--bare",
|
|
@@ -5427,13 +5481,13 @@ var GitManager = class _GitManager {
|
|
|
5427
5481
|
baseBranch: options.baseBranch,
|
|
5428
5482
|
subdirectory: options.subdirectory
|
|
5429
5483
|
});
|
|
5430
|
-
|
|
5484
|
+
log6.debug("git", "Remote initialization complete");
|
|
5431
5485
|
} finally {
|
|
5432
5486
|
if (releaseLock) {
|
|
5433
5487
|
try {
|
|
5434
5488
|
await releaseLock();
|
|
5435
5489
|
} catch (err) {
|
|
5436
|
-
|
|
5490
|
+
log6.debug("git", "Failed to release remote-init lock", { err });
|
|
5437
5491
|
}
|
|
5438
5492
|
}
|
|
5439
5493
|
remoteInitLocks.delete(options.remotePath);
|
|
@@ -5668,7 +5722,7 @@ var GitManager = class _GitManager {
|
|
|
5668
5722
|
try {
|
|
5669
5723
|
const stat = await fs16.stat(config.autoDetectRemotePath);
|
|
5670
5724
|
if (stat.isDirectory()) {
|
|
5671
|
-
|
|
5725
|
+
log6.debug("git", "Auto-detected local remote", {
|
|
5672
5726
|
path: config.autoDetectRemotePath
|
|
5673
5727
|
});
|
|
5674
5728
|
return config.autoDetectRemotePath;
|
|
@@ -5732,7 +5786,7 @@ var GitManager = class _GitManager {
|
|
|
5732
5786
|
try {
|
|
5733
5787
|
const stat = await fs16.stat(gitPath);
|
|
5734
5788
|
if (stat.isDirectory()) {
|
|
5735
|
-
|
|
5789
|
+
log6.debug("git", "Removing corrupt .git directory", {
|
|
5736
5790
|
workspacePath: options.workspacePath
|
|
5737
5791
|
});
|
|
5738
5792
|
await fs16.rm(gitPath, { recursive: true });
|
|
@@ -5753,6 +5807,10 @@ var GitManager = class _GitManager {
|
|
|
5753
5807
|
allowNetworkRemoteInProd: options.allowNetworkRemoteInProd
|
|
5754
5808
|
});
|
|
5755
5809
|
if (!remoteUrl) {
|
|
5810
|
+
const { operatingStrategy: operatingStrategy2 } = await Promise.resolve().then(() => (init_operating_mode(), operating_mode_exports));
|
|
5811
|
+
const { autoDetectRemotePath } = operatingStrategy2(options.mode).getRemoteUrlConfig();
|
|
5812
|
+
if (autoDetectRemotePath)
|
|
5813
|
+
throw new RemoteNotReadyError(autoDetectRemotePath);
|
|
5756
5814
|
throw new Error("CanopyCMS: defaultRemoteUrl (or CANOPYCMS_REMOTE_URL) is required to initialize workspace");
|
|
5757
5815
|
}
|
|
5758
5816
|
try {
|
|
@@ -5776,7 +5834,7 @@ var GitManager = class _GitManager {
|
|
|
5776
5834
|
await git.git.addConfig("canopycms.managed", "true");
|
|
5777
5835
|
await git.git.addConfig("user.name", options.gitBotAuthorName);
|
|
5778
5836
|
await git.git.addConfig("user.email", options.gitBotAuthorEmail);
|
|
5779
|
-
|
|
5837
|
+
log6.debug("git", "Marked workspace as CanopyCMS-managed", {
|
|
5780
5838
|
workspacePath: options.workspacePath
|
|
5781
5839
|
});
|
|
5782
5840
|
if (!justCloned) {
|
|
@@ -5959,6 +6017,10 @@ var GitManager = class _GitManager {
|
|
|
5959
6017
|
const fileArray = Array.isArray(files) ? files : [files];
|
|
5960
6018
|
await this.git.add(fileArray);
|
|
5961
6019
|
}
|
|
6020
|
+
/** Stage every working-tree change except canopycms's own state. See {@link stageAllExceptCanopyState}. */
|
|
6021
|
+
async addAllExceptCanopyState() {
|
|
6022
|
+
await stageAllExceptCanopyState(this.git);
|
|
6023
|
+
}
|
|
5962
6024
|
async commit(message) {
|
|
5963
6025
|
await this.git.commit(message);
|
|
5964
6026
|
}
|
|
@@ -5990,12 +6052,12 @@ var GitManager = class _GitManager {
|
|
|
5990
6052
|
* ("couldn't find remote ref"); that counts as "ahead", not an error.
|
|
5991
6053
|
*/
|
|
5992
6054
|
async hasUnpushedCommits(branch) {
|
|
5993
|
-
const target = branch ?? await this.git.revparse(["--abbrev-ref", "--end-of-options", "HEAD"]);
|
|
5994
|
-
const localSha = (await this.git.revparse(["--end-of-options", target])).trim();
|
|
6055
|
+
const target = branch ?? await this.git.revparse(["--verify", "--abbrev-ref", "--end-of-options", "HEAD"]);
|
|
6056
|
+
const localSha = (await this.git.revparse(["--verify", "--end-of-options", target])).trim();
|
|
5995
6057
|
let fetchedTip;
|
|
5996
6058
|
try {
|
|
5997
6059
|
await this.git.raw(["fetch", "--end-of-options", this.remote, target]);
|
|
5998
|
-
fetchedTip = (await this.git.revparse(["--end-of-options", "FETCH_HEAD"])).trim();
|
|
6060
|
+
fetchedTip = (await this.git.revparse(["--verify", "--end-of-options", "FETCH_HEAD"])).trim();
|
|
5999
6061
|
} catch {
|
|
6000
6062
|
return true;
|
|
6001
6063
|
}
|
|
@@ -6004,6 +6066,25 @@ var GitManager = class _GitManager {
|
|
|
6004
6066
|
const aheadCount = (await this.git.raw(["rev-list", "--count", `${fetchedTip}..${localSha}`])).trim();
|
|
6005
6067
|
return aheadCount !== "0";
|
|
6006
6068
|
}
|
|
6069
|
+
/**
|
|
6070
|
+
* Repo-relative paths that differ between HEAD and its merge base with the
|
|
6071
|
+
* base branch: everything the branch changes, across all of its commits.
|
|
6072
|
+
* Diffs against the just-fetched base tip pinned to a SHA — the pullBaseInner
|
|
6073
|
+
* constraint.
|
|
6074
|
+
*/
|
|
6075
|
+
async listChangedPathsSinceBase() {
|
|
6076
|
+
await this.git.raw(["fetch", "--end-of-options", this.remote, this.baseBranch]);
|
|
6077
|
+
const baseTip = (await this.git.revparse(["--verify", "--end-of-options", "FETCH_HEAD"])).trim();
|
|
6078
|
+
const output = await this.git.raw([
|
|
6079
|
+
"diff",
|
|
6080
|
+
"--name-only",
|
|
6081
|
+
"-z",
|
|
6082
|
+
"--no-renames",
|
|
6083
|
+
`${baseTip}...HEAD`,
|
|
6084
|
+
"--"
|
|
6085
|
+
]);
|
|
6086
|
+
return output.split("\0").filter((p) => p.length > 0);
|
|
6087
|
+
}
|
|
6007
6088
|
async ensureAuthor(author) {
|
|
6008
6089
|
const config = await this.git.listConfig();
|
|
6009
6090
|
const isManaged = config.all["canopycms.managed"] === "true";
|
|
@@ -6070,10 +6151,10 @@ var GitManager = class _GitManager {
|
|
|
6070
6151
|
}
|
|
6071
6152
|
}
|
|
6072
6153
|
async createOrphanSettingsBranchInner(branchName, initialFiles) {
|
|
6073
|
-
|
|
6154
|
+
log6.debug("git", "Creating orphan settings branch", { branchName });
|
|
6074
6155
|
const branches = await this.git.branch();
|
|
6075
6156
|
if (branches.all.includes(branchName)) {
|
|
6076
|
-
|
|
6157
|
+
log6.debug("git", "Orphan branch already exists", { branchName });
|
|
6077
6158
|
await this.git.checkout(branchName);
|
|
6078
6159
|
return;
|
|
6079
6160
|
}
|
|
@@ -6089,19 +6170,19 @@ var GitManager = class _GitManager {
|
|
|
6089
6170
|
await this.git.add(filePath);
|
|
6090
6171
|
}
|
|
6091
6172
|
await this.git.commit("Initialize settings branch", ["--allow-empty"]);
|
|
6092
|
-
|
|
6173
|
+
log6.debug("git", "Orphan settings branch created", { branchName });
|
|
6093
6174
|
}
|
|
6094
6175
|
};
|
|
6095
6176
|
|
|
6096
6177
|
// dist/branch-workspace.js
|
|
6097
|
-
var
|
|
6178
|
+
var log7 = createDebugLogger({ prefix: "BranchWorkspace" });
|
|
6098
6179
|
var workspaceInitLocks = /* @__PURE__ */ new Map();
|
|
6099
6180
|
var BranchWorkspaceManager = class {
|
|
6100
6181
|
constructor(config) {
|
|
6101
6182
|
this.config = config;
|
|
6102
6183
|
}
|
|
6103
6184
|
async ensureGitWorkspace(options) {
|
|
6104
|
-
return
|
|
6185
|
+
return log7.timed("workspace", "ensureGitWorkspace", async () => {
|
|
6105
6186
|
const existingLock = workspaceInitLocks.get(options.branchRoot);
|
|
6106
6187
|
if (existingLock) {
|
|
6107
6188
|
await existingLock;
|
|
@@ -6110,11 +6191,11 @@ var BranchWorkspaceManager = class {
|
|
|
6110
6191
|
const lockPromise = (async () => {
|
|
6111
6192
|
let releaseLock;
|
|
6112
6193
|
try {
|
|
6113
|
-
|
|
6194
|
+
log7.debug("workspace", "Ensuring git workspace", {
|
|
6114
6195
|
branchName: options.branchName,
|
|
6115
6196
|
mode: options.mode
|
|
6116
6197
|
});
|
|
6117
|
-
releaseLock = await acquireProvisioningLock(path18.dirname(options.branchRoot),
|
|
6198
|
+
releaseLock = await acquireProvisioningLock(path18.dirname(options.branchRoot), branchProvisioningLockName(path18.basename(options.branchRoot)));
|
|
6118
6199
|
await GitManager.initializeWorkspace({
|
|
6119
6200
|
workspacePath: options.branchRoot,
|
|
6120
6201
|
branchName: options.branchName,
|
|
@@ -6135,7 +6216,7 @@ var BranchWorkspaceManager = class {
|
|
|
6135
6216
|
try {
|
|
6136
6217
|
await releaseLock();
|
|
6137
6218
|
} catch (err) {
|
|
6138
|
-
|
|
6219
|
+
log7.debug("workspace", "Failed to release workspace-init lock", { err });
|
|
6139
6220
|
}
|
|
6140
6221
|
}
|
|
6141
6222
|
workspaceInitLocks.delete(options.branchRoot);
|
|
@@ -6243,7 +6324,7 @@ import fs17 from "node:fs/promises";
|
|
|
6243
6324
|
import path19 from "node:path";
|
|
6244
6325
|
import matter3 from "gray-matter";
|
|
6245
6326
|
import { parse as yamlParse2 } from "yaml";
|
|
6246
|
-
var
|
|
6327
|
+
var log8 = createDebugLogger({ prefix: "ContentListing" });
|
|
6247
6328
|
var normalizePath = (root, target) => {
|
|
6248
6329
|
const result = validateAndNormalizePath(root, target);
|
|
6249
6330
|
if (!result.valid) {
|
|
@@ -6269,7 +6350,7 @@ var readEntryData = async (filePath, format, bodyFieldName = "body") => {
|
|
|
6269
6350
|
} catch (err) {
|
|
6270
6351
|
if (isNotFoundError(err))
|
|
6271
6352
|
return {};
|
|
6272
|
-
|
|
6353
|
+
log8.warn("readEntryData", `Failed to read entry data from ${filePath}: ${getErrorMessage(err)}`);
|
|
6273
6354
|
return {};
|
|
6274
6355
|
}
|
|
6275
6356
|
};
|
|
@@ -6297,7 +6378,7 @@ async function listEntries(branchRoot, flatSchema, contentRootName, options, vis
|
|
|
6297
6378
|
const resolver = options?.resolveReferences ? createReferenceResolver(branchRoot, flatSchema, contentRootName) : null;
|
|
6298
6379
|
const collectionResults = await Promise.all(collections.map(async (collection) => {
|
|
6299
6380
|
const entries = await listCollectionEntries(branchRoot, collection, (file) => skippedFiles.push(file));
|
|
6300
|
-
const visible = shouldInclude ? entries.filter((e) => shouldInclude(e.
|
|
6381
|
+
const visible = shouldInclude ? entries.filter((e) => shouldInclude(e.logicalPath)) : entries;
|
|
6301
6382
|
const resolved = resolver ? await resolveCollectionItemReferences(visible, collection, resolver) : visible;
|
|
6302
6383
|
return resolved.map((entry) => ({ entry, collection }));
|
|
6303
6384
|
}));
|
|
@@ -6387,7 +6468,7 @@ var listCollectionEntries = async (root, collection, onSkip) => {
|
|
|
6387
6468
|
const relativePath = normalizePath(root, absolutePath);
|
|
6388
6469
|
const parsed = parseTypedFilename(file.name, entryTypes);
|
|
6389
6470
|
if (!parsed) {
|
|
6390
|
-
|
|
6471
|
+
log8.warn("listCollectionEntries", `Skipping file with unrecognized filename format: ${file.name} (expected {type}.{slug}.{id}.{ext} with a known entry type and valid 12-char Base58 ID)`);
|
|
6391
6472
|
if (looksLikeMalformedEntry(file.name, entryTypes)) {
|
|
6392
6473
|
onSkip?.({ filename: file.name, collectionPath: collection.logicalPath });
|
|
6393
6474
|
}
|
|
@@ -6402,7 +6483,7 @@ var listCollectionEntries = async (root, collection, onSkip) => {
|
|
|
6402
6483
|
readEntryData(absolutePath, format, bodyField)
|
|
6403
6484
|
]);
|
|
6404
6485
|
const item = {
|
|
6405
|
-
logicalPath:
|
|
6486
|
+
logicalPath: entryLogicalPath(collection.logicalPath, slug),
|
|
6406
6487
|
contentId,
|
|
6407
6488
|
slug,
|
|
6408
6489
|
collectionPath: collection.logicalPath,
|