canopycms 0.0.66-int.85 → 0.0.66

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/cli/cli.js CHANGED
@@ -93,6 +93,7 @@ import { isAbsolute } from "pathe";
93
93
  import { z as z3 } from "zod";
94
94
  import { z as z4 } from "zod";
95
95
  import { z as z5 } from "zod";
96
+ import { z as z6 } from "zod";
96
97
  import { join, normalize } from "pathe";
97
98
  import fs2 from "node:fs/promises";
98
99
  import fs22 from "node:fs/promises";
@@ -115,6 +116,47 @@ import path8 from "node:path";
115
116
  function canopyLogWarn(...args) {
116
117
  active.warn(...args);
117
118
  }
119
+ function declaresScheme(url) {
120
+ return /^[a-z][a-z0-9+.-]*:/i.test(url.trim());
121
+ }
122
+ function isImplicitlyOffOrigin(url) {
123
+ if (declaresScheme(url))
124
+ return false;
125
+ try {
126
+ return new URL(url, SENTINEL_BASE).origin !== SENTINEL_BASE;
127
+ } catch {
128
+ return false;
129
+ }
130
+ }
131
+ function isHttpUrlOrSameOriginPath(value, opts = {}) {
132
+ if (value === "")
133
+ return false;
134
+ for (let index = 0; index < value.length; index++) {
135
+ const code = value.charCodeAt(index);
136
+ if (code <= 32 || code === 127)
137
+ return false;
138
+ }
139
+ if (value.includes("?") || value.includes("#"))
140
+ return false;
141
+ if (value.includes("\\"))
142
+ return false;
143
+ if (DOT_SEGMENT.test(value))
144
+ return false;
145
+ if (declaresScheme(value)) {
146
+ if (!/^https?:\/\//i.test(value))
147
+ return false;
148
+ try {
149
+ new URL(value);
150
+ return true;
151
+ } catch {
152
+ return false;
153
+ }
154
+ }
155
+ if (isImplicitlyOffOrigin(value)) {
156
+ return opts.allowProtocolRelative === true && value.startsWith("//") && value[2] !== "/";
157
+ }
158
+ return value.startsWith("/") && value[1] !== "/";
159
+ }
118
160
  function getErrorMessage2(err) {
119
161
  if (err instanceof Error) {
120
162
  return err.message;
@@ -1074,7 +1116,7 @@ CanopyCMS worker run-once (mode: ${mode}, auth: ${authMode})
1074
1116
  }
1075
1117
  console.log("\nDone");
1076
1118
  }
1077
- var __defProp2, __getOwnPropNames2, __esm2, __export2, active, init_logger, 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, basePathSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE, init_config, permissionTargetSchema, pathPermissionSchema, init_permissions, init_normalize, init_types2, init_flatten, init_mode_env, init_validation, init_helpers, init_config2, init_error2, init_fs2, templates_exports, __dirname, TEMPLATES_DIR, SCAFFOLD_STACK_NAME, init_templates, project_detect_exports, COMMANDS, MAX_WORKSPACE_DEPTH, CDK_DEPENDENCIES, init_project_detect, task_queue_config_exports, init_task_queue_config, LOG_LEVELS, DebugLogger, testLogger, init_debug, init_atomic_write, DEFAULT_MAX_RETRIES, nullLogger, init_task_queue, init_task_queue2, task_queue_exports, debugLogger, cmsTaskQueueLogger, init_task_queue3, ProdClientSafeStrategy, DevClientSafeStrategy, warned, VALID_DEPLOYMENT_NAME, isValidDeploymentName, ProdStrategy, DevStrategy, strategyCache;
1119
+ var __defProp2, __getOwnPropNames2, __esm2, __export2, active, init_logger, 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, SENTINEL_BASE, DOT_SEGMENT, init_sanitize_href, uploadTargetUrlSchema, assetMountUrlSchema, init_url, mediaSchema, init_media, defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, basePathSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE, init_config, permissionTargetSchema, pathPermissionSchema, init_permissions, init_normalize, init_types2, init_flatten, init_mode_env, init_validation, init_helpers, init_config2, init_error2, init_fs2, templates_exports, __dirname, TEMPLATES_DIR, SCAFFOLD_STACK_NAME, init_templates, project_detect_exports, COMMANDS, MAX_WORKSPACE_DEPTH, CDK_DEPENDENCIES, init_project_detect, task_queue_config_exports, init_task_queue_config, LOG_LEVELS, DebugLogger, testLogger, init_debug, init_atomic_write, DEFAULT_MAX_RETRIES, nullLogger, init_task_queue, init_task_queue2, task_queue_exports, debugLogger, cmsTaskQueueLogger, init_task_queue3, ProdClientSafeStrategy, DevClientSafeStrategy, warned, VALID_DEPLOYMENT_NAME, isValidDeploymentName, ProdStrategy, DevStrategy, strategyCache;
1078
1120
  var init_init = __esm({
1079
1121
  "dist/cli/init.js"() {
1080
1122
  "use strict";
@@ -1238,29 +1280,64 @@ var init_init = __esm({
1238
1280
  }));
1239
1281
  }
1240
1282
  });
1283
+ init_sanitize_href = __esm2({
1284
+ "dist/utils/sanitize-href.js"() {
1285
+ "use strict";
1286
+ SENTINEL_BASE = "https://relative.invalid";
1287
+ DOT_SEGMENT = /(^|\/)(\.|%2e){1,2}(\/|$)/i;
1288
+ }
1289
+ });
1290
+ init_url = __esm2({
1291
+ "dist/config/schemas/url.js"() {
1292
+ "use strict";
1293
+ init_sanitize_href();
1294
+ uploadTargetUrlSchema = z3.string().trim().refine((value) => isHttpUrlOrSameOriginPath(value), {
1295
+ message: 'must be an absolute http(s) URL or a site-relative path beginning with a single "/", with no query or fragment'
1296
+ });
1297
+ assetMountUrlSchema = z3.string().trim().refine((value) => isHttpUrlOrSameOriginPath(value, { allowProtocolRelative: true }), {
1298
+ message: 'must be an absolute http(s) URL, a protocol-relative "//host" URL, or a site-relative path beginning with a single "/", with no query or fragment'
1299
+ });
1300
+ }
1301
+ });
1241
1302
  init_media = __esm2({
1242
1303
  "dist/config/schemas/media.js"() {
1243
1304
  "use strict";
1244
1305
  init_collection();
1245
- mediaSchema = z3.discriminatedUnion("adapter", [
1246
- z3.object({
1247
- adapter: z3.literal("local"),
1248
- publicBaseUrl: z3.string().url().optional(),
1306
+ init_url();
1307
+ mediaSchema = z4.discriminatedUnion("adapter", [
1308
+ z4.object({
1309
+ adapter: z4.literal("local"),
1310
+ publicBaseUrl: assetMountUrlSchema.optional(),
1249
1311
  /** Root directory for local asset storage. Defaults to the caller's dev-assets dir. */
1250
1312
  directory: relativePathSchema.optional()
1251
- }),
1252
- z3.object({
1253
- adapter: z3.literal("s3"),
1254
- bucket: z3.string().min(1),
1255
- region: z3.string().min(1),
1256
- publicBaseUrl: z3.string().url().optional(),
1313
+ }).strict(),
1314
+ z4.object({
1315
+ adapter: z4.literal("s3"),
1316
+ bucket: z4.string().min(1),
1317
+ region: z4.string().min(1),
1318
+ publicBaseUrl: assetMountUrlSchema.optional(),
1319
+ /**
1320
+ * Where the browser POSTs a presigned direct upload. Absolute http(s) URL or a
1321
+ * site-relative path; defaults to the S3 REST endpoint the AWS SDK returns.
1322
+ *
1323
+ * Set this to route uploads through your own CDN so they are same-origin with the
1324
+ * editor, which removes the need for a bucket CORS rule naming an exact origin. The
1325
+ * signature is unaffected — a presigned POST's string-to-sign is the base64 policy
1326
+ * alone, so the host never enters it (pinned by store-s3.test.ts).
1327
+ *
1328
+ * NOT a prefix. Unlike `publicBaseUrl`, nothing is joined onto this value; it replaces
1329
+ * the POST target outright and is passed through byte-for-byte. Do not route it through
1330
+ * `joinUrlPrefix` "for consistency" — that would strip the trailing slash and silently
1331
+ * stop a CDN path pattern like `/asset-upload/*` from matching.
1332
+ */
1333
+ uploadUrl: uploadTargetUrlSchema.optional(),
1257
1334
  /** Max upload size in bytes for presigned direct uploads. Defaults to 50 MiB. */
1258
- maxUploadBytes: z3.number().int().positive().optional()
1259
- }),
1260
- z3.object({
1261
- adapter: z3.literal("lfs"),
1262
- publicBaseUrl: z3.string().url().optional()
1263
- })
1335
+ maxUploadBytes: z4.number().int().positive().optional()
1336
+ }).strict(),
1337
+ z4.object({
1338
+ adapter: z4.literal("lfs"),
1339
+ publicBaseUrl: assetMountUrlSchema.optional()
1340
+ }).strict()
1264
1341
  ]);
1265
1342
  }
1266
1343
  });
@@ -1269,44 +1346,44 @@ var init_init = __esm({
1269
1346
  "use strict";
1270
1347
  init_collection();
1271
1348
  init_media();
1272
- defaultBranchAccessSchema = z4.enum(["allow", "deny"]).default("deny");
1273
- defaultPathAccessLevelSchema = z4.enum(["allow", "deny"]);
1274
- defaultPathAccessLevelsSchema = z4.object({
1349
+ defaultBranchAccessSchema = z5.enum(["allow", "deny"]).default("deny");
1350
+ defaultPathAccessLevelSchema = z5.enum(["allow", "deny"]);
1351
+ defaultPathAccessLevelsSchema = z5.object({
1275
1352
  read: defaultPathAccessLevelSchema.optional(),
1276
1353
  edit: defaultPathAccessLevelSchema.optional(),
1277
1354
  review: defaultPathAccessLevelSchema.optional()
1278
1355
  }).strict();
1279
- defaultPathAccessSchema = z4.union([defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema]).default("deny");
1280
- defaultBaseBranchSchema = z4.string().default("main");
1281
- defaultRemoteNameSchema = z4.string().default("origin");
1282
- defaultRemoteUrlSchema = z4.string().min(1);
1283
- gitBotAuthorNameSchema = z4.string().min(1);
1284
- gitBotAuthorEmailSchema = z4.string().email();
1285
- githubTokenEnvVarSchema = z4.string().default("GITHUB_BOT_TOKEN");
1286
- operatingModeSchema = z4.enum(["prod", "dev"]);
1287
- deployedAsSchema = z4.enum(["static", "server"]).default("server");
1356
+ defaultPathAccessSchema = z5.union([defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema]).default("deny");
1357
+ defaultBaseBranchSchema = z5.string().default("main");
1358
+ defaultRemoteNameSchema = z5.string().default("origin");
1359
+ defaultRemoteUrlSchema = z5.string().min(1);
1360
+ gitBotAuthorNameSchema = z5.string().min(1);
1361
+ gitBotAuthorEmailSchema = z5.string().email();
1362
+ githubTokenEnvVarSchema = z5.string().default("GITHUB_BOT_TOKEN");
1363
+ operatingModeSchema = z5.enum(["prod", "dev"]);
1364
+ deployedAsSchema = z5.enum(["static", "server"]).default("server");
1288
1365
  contentRootSchema = relativePathSchema.default("content");
1289
- sourceRootSchema = z4.string().min(1).optional();
1290
- basePathSchema = z4.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
1366
+ sourceRootSchema = z5.string().min(1).optional();
1367
+ basePathSchema = z5.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
1291
1368
  message: "basePath must not be empty or only whitespace"
1292
1369
  }).optional();
1293
- deploymentNameSchema = z4.string().default("prod");
1294
- devContentSyncSchema = z4.enum(["off", "warn"]).default("warn");
1295
- devConfigSchema = z4.object({
1370
+ deploymentNameSchema = z5.string().default("prod");
1371
+ devContentSyncSchema = z5.enum(["off", "warn"]).default("warn");
1372
+ devConfigSchema = z5.object({
1296
1373
  contentSync: devContentSyncSchema.optional()
1297
1374
  });
1298
- editorConfigSchema = z4.object({
1299
- title: z4.string().optional(),
1300
- subtitle: z4.string().optional(),
1301
- theme: z4.unknown().optional(),
1302
- previewBase: z4.record(z4.string()).optional(),
1375
+ editorConfigSchema = z5.object({
1376
+ title: z5.string().optional(),
1377
+ subtitle: z5.string().optional(),
1378
+ theme: z5.unknown().optional(),
1379
+ previewBase: z5.record(z5.string()).optional(),
1303
1380
  // UI handler functions (runtime only, don't serialize)
1304
- onAccountClick: z4.function().returns(z4.void()).optional(),
1305
- onLogoutClick: z4.function().returns(z4.void()).optional(),
1381
+ onAccountClick: z5.function().returns(z5.void()).optional(),
1382
+ onLogoutClick: z5.function().returns(z5.void()).optional(),
1306
1383
  // Optional: custom account component (e.g., Clerk's UserButton)
1307
- AccountComponent: z4.custom().optional()
1384
+ AccountComponent: z5.custom().optional()
1308
1385
  });
1309
- CanopyConfigSchema = z4.object({
1386
+ CanopyConfigSchema = z5.object({
1310
1387
  media: mediaSchema.optional(),
1311
1388
  // No outer .optional() here: defaultBranchAccessSchema/defaultPathAccessSchema already
1312
1389
  // have .default('deny'), so the field is optional on input but always resolves to
@@ -1322,7 +1399,7 @@ var init_init = __esm({
1322
1399
  // unset must stay undefined after parsing so dev mode can detect the fork
1323
1400
  // point from git HEAD (see resolveBaseBranch in utils/git.ts).
1324
1401
  defaultBaseBranch: defaultBaseBranchSchema.optional(),
1325
- defaultActiveBranch: z4.string().min(1).optional(),
1402
+ defaultActiveBranch: z5.string().min(1).optional(),
1326
1403
  defaultRemoteName: defaultRemoteNameSchema.optional(),
1327
1404
  defaultRemoteUrl: defaultRemoteUrlSchema.optional(),
1328
1405
  gitBotAuthorName: gitBotAuthorNameSchema,
@@ -1336,9 +1413,9 @@ var init_init = __esm({
1336
1413
  // Escape hatch for prod hosts that genuinely have internet access and intentionally
1337
1414
  // run git against a network remote (see GitManager.resolveRemoteUrl's prod-mode guard).
1338
1415
  // Default false/unset — the standard AWS Lambda+worker topology must leave this unset.
1339
- allowNetworkRemoteInProd: z4.boolean().optional(),
1340
- settingsBranch: z4.string().optional(),
1341
- autoCreateSettingsPR: z4.boolean().optional(),
1416
+ allowNetworkRemoteInProd: z5.boolean().optional(),
1417
+ settingsBranch: z5.string().optional(),
1418
+ autoCreateSettingsPR: z5.boolean().optional(),
1342
1419
  // Deliberately `deploymentNameSchema.optional()`, NOT `deploymentNameSchema` alone.
1343
1420
  // deploymentNameSchema.default('prod') would make `parse(undefined)` resolve to
1344
1421
  // the literal 'prod' instead of staying `undefined` — collapsing the
@@ -1354,9 +1431,9 @@ var init_init = __esm({
1354
1431
  sourceRoot: sourceRootSchema.optional(),
1355
1432
  basePath: basePathSchema,
1356
1433
  editor: editorConfigSchema.optional(),
1357
- authPlugin: z4.custom().optional(),
1358
- entryLinkUrl: z4.custom().optional(),
1359
- validateEntry: z4.custom().optional(),
1434
+ authPlugin: z5.custom().optional(),
1435
+ entryLinkUrl: z5.custom().optional(),
1436
+ validateEntry: z5.custom().optional(),
1360
1437
  dev: devConfigSchema.optional()
1361
1438
  }).strict();
1362
1439
  DEFAULT_PROD_WORKSPACE = "/mnt/efs/workspace";
@@ -1365,12 +1442,12 @@ var init_init = __esm({
1365
1442
  init_permissions = __esm2({
1366
1443
  "dist/config/schemas/permissions.js"() {
1367
1444
  "use strict";
1368
- permissionTargetSchema = z5.object({
1369
- allowedUsers: z5.array(z5.string()).optional(),
1370
- allowedGroups: z5.array(z5.string()).optional()
1445
+ permissionTargetSchema = z6.object({
1446
+ allowedUsers: z6.array(z6.string()).optional(),
1447
+ allowedGroups: z6.array(z6.string()).optional()
1371
1448
  });
1372
- pathPermissionSchema = z5.object({
1373
- path: z5.string().min(1),
1449
+ pathPermissionSchema = z6.object({
1450
+ path: z6.string().min(1),
1374
1451
  read: permissionTargetSchema.optional(),
1375
1452
  edit: permissionTargetSchema.optional(),
1376
1453
  review: permissionTargetSchema.optional()
@@ -1880,7 +1957,8 @@ import { z as z32 } from "zod";
1880
1957
  import { isAbsolute as isAbsolute2 } from "pathe";
1881
1958
  import { z as z42 } from "zod";
1882
1959
  import { z as z52 } from "zod";
1883
- import { z as z6 } from "zod";
1960
+ import { z as z62 } from "zod";
1961
+ import { z as z7 } from "zod";
1884
1962
  import path9 from "node:path";
1885
1963
  import path21 from "node:path";
1886
1964
  import { createJiti as createJiti2 } from "jiti";
@@ -1911,7 +1989,7 @@ import fs72 from "node:fs/promises";
1911
1989
  import path82 from "node:path";
1912
1990
  import { promises as fs62 } from "fs";
1913
1991
  import { join as join3 } from "pathe";
1914
- import { z as z7 } from "zod";
1992
+ import { z as z8 } from "zod";
1915
1993
  import chokidar from "chokidar";
1916
1994
  import fs8 from "node:fs/promises";
1917
1995
  import { sep, resolve, relative } from "node:path";
@@ -1945,6 +2023,47 @@ function canopyLogWarn2(...args) {
1945
2023
  function canopyLogError(...args) {
1946
2024
  active2.error(...args);
1947
2025
  }
2026
+ function declaresScheme2(url) {
2027
+ return /^[a-z][a-z0-9+.-]*:/i.test(url.trim());
2028
+ }
2029
+ function isImplicitlyOffOrigin2(url) {
2030
+ if (declaresScheme2(url))
2031
+ return false;
2032
+ try {
2033
+ return new URL(url, SENTINEL_BASE2).origin !== SENTINEL_BASE2;
2034
+ } catch {
2035
+ return false;
2036
+ }
2037
+ }
2038
+ function isHttpUrlOrSameOriginPath2(value, opts = {}) {
2039
+ if (value === "")
2040
+ return false;
2041
+ for (let index = 0; index < value.length; index++) {
2042
+ const code = value.charCodeAt(index);
2043
+ if (code <= 32 || code === 127)
2044
+ return false;
2045
+ }
2046
+ if (value.includes("?") || value.includes("#"))
2047
+ return false;
2048
+ if (value.includes("\\"))
2049
+ return false;
2050
+ if (DOT_SEGMENT2.test(value))
2051
+ return false;
2052
+ if (declaresScheme2(value)) {
2053
+ if (!/^https?:\/\//i.test(value))
2054
+ return false;
2055
+ try {
2056
+ new URL(value);
2057
+ return true;
2058
+ } catch {
2059
+ return false;
2060
+ }
2061
+ }
2062
+ if (isImplicitlyOffOrigin2(value)) {
2063
+ return opts.allowProtocolRelative === true && value.startsWith("//") && value[2] !== "/";
2064
+ }
2065
+ return value.startsWith("/") && value[1] !== "/";
2066
+ }
1948
2067
  function normalizeFilesystemPath(path222) {
1949
2068
  return path222.split(/[\\/]+/).filter(Boolean).join("/");
1950
2069
  }
@@ -4567,7 +4686,7 @@ async function generateAIContentCLI(options) {
4567
4686
  console.log(` Output: ${result.outputDir}
4568
4687
  `);
4569
4688
  }
4570
- var __defProp3, __getOwnPropNames3, __esm3, __export3, active2, init_logger2, init_normalize2, ROOT_COLLECTION_ID, init_types3, normalizePathValue, flattenSchema, init_flatten2, primitiveFieldTypes2, fieldTypes2, init_types22, fieldBaseSchema2, selectOptionSchema2, referenceOptionSchema2, primitiveFieldSchema2, ASPECT_RATIO_RE2, imageFieldSchema2, selectFieldSchema2, referenceFieldSchema2, fieldHolder2, blockSchema2, blockFieldSchema2, objectFieldSchema2, inlineGroupFieldSchema2, customFieldSchema2, knownFieldSchema2, fieldSchema2, init_field2, relativePathSchema2, entryTypeSchema2, collectionSchema2, init_collection2, mediaSchema2, init_media2, defaultBranchAccessSchema2, defaultPathAccessLevelSchema2, defaultPathAccessLevelsSchema2, defaultPathAccessSchema2, defaultBaseBranchSchema2, defaultRemoteNameSchema2, defaultRemoteUrlSchema2, gitBotAuthorNameSchema2, gitBotAuthorEmailSchema2, githubTokenEnvVarSchema2, operatingModeSchema2, deployedAsSchema2, contentRootSchema2, sourceRootSchema2, basePathSchema2, deploymentNameSchema2, devContentSyncSchema2, devConfigSchema2, editorConfigSchema2, CanopyConfigSchema2, DEFAULT_PROD_WORKSPACE2, init_config3, SERVER_MODE_ENV_VAR, BROWSER_MODE_ENV_VAR, warned2, init_mode_env2, forEachReferenceField, init_validation2, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned22, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, permissionTargetSchema2, pathPermissionSchema2, init_permissions2, init_helpers2, init_config22, ProdStrategy2, DevStrategy2, strategyCache2, init_client_unsafe_strategy, operating_mode_exports, init_operating_mode, BLOCK_DISCRIMINATOR_KEYS, BLOCK_STRUCTURAL_KEYS, EVIDENCE_MAX_DEPTH, locks, META_DIR, CONTENT_WRITE_LOCK_NAME, DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, ContentWriteLockBusyError, sleep, DEFAULT_SEO_FIELD_NAMES, SEO_GROUP_FIELDS, BASE58_PATTERN, CONTENT_ID_PATTERN, PHYSICAL_SEGMENT_PATTERN, isValidId, EMPTY_LOGICAL_PATH, ContentIdIndex, parseTypedFilename, CONTENT_EXTENSIONS, registry, finalization, LOG_LEVELS2, DebugLogger2, testLogger2, log2, META_DIR2, entryTypeMetaSchema, collectionMetaSchema, rootCollectionMetaSchema, editDistance, closestName, forEachEntryType, collectEntryTypeNames, validateReferenceEntryTypes, isDeployedStatic, isBuildMode, STATIC_DEPLOY_USER, SCHEMA_CACHE_VERSION, MTIME_CHECK_DEBOUNCE_MS, SCHEMA_GENERATION_RESOURCE, BranchSchemaCache, RESOURCE, getFormatExtension, isDataOnlyFormat, asRecord, BranchPathError, resolveContentBranchesRoot, ContentStoreError, ContentConflictError, BranchSyncingError, DuplicateContentIdError, UrlPathConflictError, DEFAULT_INDEX_FRESHNESS_INTERVAL_MS, FORCED_REFRESH_MIN_INTERVAL_MS, STALE_LOOKUP, createReferenceResolveCache, 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, BRANCH_META_DIR, BRANCH_META_FILE, BranchMetadataCorruptError, branchMetadataFilePath, log3, REGISTRY_FILE, REGISTRY_STALE_FILE, REGISTRY_TEMP_FILE, REGISTRY_VERSION, RESOURCE2, GET_MISS_REFRESH_MIN_INTERVAL_MS, BranchRegistry, log4, OccWriteConflictError, CURRENT_SCHEMA_VERSION, BranchMetadataConflictError, BranchMetadataFileManager, getBranchMetadataFileManager, loadBranchContext, NETWORK_SCHEME_PATTERN, TRANSPORT_HELPER_PATTERN, LEADING_OPTION_PATTERN, WINDOWS_DRIVE_PATTERN, COLON_BEFORE_SLASH_PATTERN, MISSING_REMOTE_REF_REASONS, log5, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, GitConflictError, GitRemoteRefMissingError, GitManager, log6, workspaceInitLocks, BranchWorkspaceManager, log7, normalizePath, readEntryData, createReferenceResolver, resolveCollectionItemReferences, looksLikeMalformedEntry, listCollectionEntries, MAX_INPUT_PIXELS, KNOWN_FIELD_TYPES, STRING_FIELD_TYPES, selectOptionValues, GENERATED_RECORD_FILENAME, SAFE_BUILD_ID, BUILD_ID_RULE, jiti;
4689
+ var __defProp3, __getOwnPropNames3, __esm3, __export3, active2, init_logger2, SENTINEL_BASE2, DOT_SEGMENT2, init_sanitize_href2, init_normalize2, ROOT_COLLECTION_ID, init_types3, normalizePathValue, flattenSchema, init_flatten2, primitiveFieldTypes2, fieldTypes2, init_types22, fieldBaseSchema2, selectOptionSchema2, referenceOptionSchema2, primitiveFieldSchema2, ASPECT_RATIO_RE2, imageFieldSchema2, selectFieldSchema2, referenceFieldSchema2, fieldHolder2, blockSchema2, blockFieldSchema2, objectFieldSchema2, inlineGroupFieldSchema2, customFieldSchema2, knownFieldSchema2, fieldSchema2, init_field2, relativePathSchema2, entryTypeSchema2, collectionSchema2, init_collection2, uploadTargetUrlSchema2, assetMountUrlSchema2, init_url2, mediaSchema2, init_media2, defaultBranchAccessSchema2, defaultPathAccessLevelSchema2, defaultPathAccessLevelsSchema2, defaultPathAccessSchema2, defaultBaseBranchSchema2, defaultRemoteNameSchema2, defaultRemoteUrlSchema2, gitBotAuthorNameSchema2, gitBotAuthorEmailSchema2, githubTokenEnvVarSchema2, operatingModeSchema2, deployedAsSchema2, contentRootSchema2, sourceRootSchema2, basePathSchema2, deploymentNameSchema2, devContentSyncSchema2, devConfigSchema2, editorConfigSchema2, CanopyConfigSchema2, DEFAULT_PROD_WORKSPACE2, init_config3, SERVER_MODE_ENV_VAR, BROWSER_MODE_ENV_VAR, warned2, init_mode_env2, forEachReferenceField, init_validation2, ProdClientSafeStrategy2, DevClientSafeStrategy2, clientStrategyCache, init_client_safe_strategy, warned22, VALID_DEPLOYMENT_NAME2, isValidDeploymentName2, init_deployment_name, permissionTargetSchema2, pathPermissionSchema2, init_permissions2, init_helpers2, init_config22, ProdStrategy2, DevStrategy2, strategyCache2, init_client_unsafe_strategy, operating_mode_exports, init_operating_mode, BLOCK_DISCRIMINATOR_KEYS, BLOCK_STRUCTURAL_KEYS, EVIDENCE_MAX_DEPTH, locks, META_DIR, CONTENT_WRITE_LOCK_NAME, DEFAULT_CONTENT_WRITE_LOCK_WAIT_MS, ContentWriteLockBusyError, sleep, DEFAULT_SEO_FIELD_NAMES, SEO_GROUP_FIELDS, BASE58_PATTERN, CONTENT_ID_PATTERN, PHYSICAL_SEGMENT_PATTERN, isValidId, EMPTY_LOGICAL_PATH, ContentIdIndex, parseTypedFilename, CONTENT_EXTENSIONS, registry, finalization, LOG_LEVELS2, DebugLogger2, testLogger2, log2, META_DIR2, entryTypeMetaSchema, collectionMetaSchema, rootCollectionMetaSchema, editDistance, closestName, forEachEntryType, collectEntryTypeNames, validateReferenceEntryTypes, isDeployedStatic, isBuildMode, STATIC_DEPLOY_USER, SCHEMA_CACHE_VERSION, MTIME_CHECK_DEBOUNCE_MS, SCHEMA_GENERATION_RESOURCE, BranchSchemaCache, RESOURCE, getFormatExtension, isDataOnlyFormat, asRecord, BranchPathError, resolveContentBranchesRoot, ContentStoreError, ContentConflictError, BranchSyncingError, DuplicateContentIdError, UrlPathConflictError, DEFAULT_INDEX_FRESHNESS_INTERVAL_MS, FORCED_REFRESH_MIN_INTERVAL_MS, STALE_LOOKUP, createReferenceResolveCache, 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, BRANCH_META_DIR, BRANCH_META_FILE, BranchMetadataCorruptError, branchMetadataFilePath, log3, REGISTRY_FILE, REGISTRY_STALE_FILE, REGISTRY_TEMP_FILE, REGISTRY_VERSION, RESOURCE2, GET_MISS_REFRESH_MIN_INTERVAL_MS, BranchRegistry, log4, OccWriteConflictError, CURRENT_SCHEMA_VERSION, BranchMetadataConflictError, BranchMetadataFileManager, getBranchMetadataFileManager, loadBranchContext, NETWORK_SCHEME_PATTERN, TRANSPORT_HELPER_PATTERN, LEADING_OPTION_PATTERN, WINDOWS_DRIVE_PATTERN, COLON_BEFORE_SLASH_PATTERN, MISSING_REMOTE_REF_REASONS, log5, GIT_ENV_PASSTHROUGH, FORCE_C_LOCALE, remoteInitLocks, GITHUB_TRACKING_REF_PREFIX, GitConflictError, GitRemoteRefMissingError, GitManager, log6, workspaceInitLocks, BranchWorkspaceManager, log7, normalizePath, readEntryData, createReferenceResolver, resolveCollectionItemReferences, looksLikeMalformedEntry, listCollectionEntries, MAX_INPUT_PIXELS, KNOWN_FIELD_TYPES, STRING_FIELD_TYPES, selectOptionValues, GENERATED_RECORD_FILENAME, SAFE_BUILD_ID, BUILD_ID_RULE, jiti;
4571
4690
  var init_generate_ai_content = __esm({
4572
4691
  "dist/cli/generate-ai-content.js"() {
4573
4692
  "use strict";
@@ -4586,6 +4705,13 @@ var init_generate_ai_content = __esm({
4586
4705
  active2 = console;
4587
4706
  }
4588
4707
  });
4708
+ init_sanitize_href2 = __esm3({
4709
+ "dist/utils/sanitize-href.js"() {
4710
+ "use strict";
4711
+ SENTINEL_BASE2 = "https://relative.invalid";
4712
+ DOT_SEGMENT2 = /(^|\/)(\.|%2e){1,2}(\/|$)/i;
4713
+ }
4714
+ });
4589
4715
  init_normalize2 = __esm3({
4590
4716
  "dist/paths/normalize.js"() {
4591
4717
  "use strict";
@@ -4841,29 +4967,57 @@ var init_generate_ai_content = __esm({
4841
4967
  }));
4842
4968
  }
4843
4969
  });
4970
+ init_url2 = __esm3({
4971
+ "dist/config/schemas/url.js"() {
4972
+ "use strict";
4973
+ init_sanitize_href2();
4974
+ uploadTargetUrlSchema2 = z42.string().trim().refine((value) => isHttpUrlOrSameOriginPath2(value), {
4975
+ message: 'must be an absolute http(s) URL or a site-relative path beginning with a single "/", with no query or fragment'
4976
+ });
4977
+ assetMountUrlSchema2 = z42.string().trim().refine((value) => isHttpUrlOrSameOriginPath2(value, { allowProtocolRelative: true }), {
4978
+ message: 'must be an absolute http(s) URL, a protocol-relative "//host" URL, or a site-relative path beginning with a single "/", with no query or fragment'
4979
+ });
4980
+ }
4981
+ });
4844
4982
  init_media2 = __esm3({
4845
4983
  "dist/config/schemas/media.js"() {
4846
4984
  "use strict";
4847
4985
  init_collection2();
4848
- mediaSchema2 = z42.discriminatedUnion("adapter", [
4849
- z42.object({
4850
- adapter: z42.literal("local"),
4851
- publicBaseUrl: z42.string().url().optional(),
4986
+ init_url2();
4987
+ mediaSchema2 = z52.discriminatedUnion("adapter", [
4988
+ z52.object({
4989
+ adapter: z52.literal("local"),
4990
+ publicBaseUrl: assetMountUrlSchema2.optional(),
4852
4991
  /** Root directory for local asset storage. Defaults to the caller's dev-assets dir. */
4853
4992
  directory: relativePathSchema2.optional()
4854
- }),
4855
- z42.object({
4856
- adapter: z42.literal("s3"),
4857
- bucket: z42.string().min(1),
4858
- region: z42.string().min(1),
4859
- publicBaseUrl: z42.string().url().optional(),
4993
+ }).strict(),
4994
+ z52.object({
4995
+ adapter: z52.literal("s3"),
4996
+ bucket: z52.string().min(1),
4997
+ region: z52.string().min(1),
4998
+ publicBaseUrl: assetMountUrlSchema2.optional(),
4999
+ /**
5000
+ * Where the browser POSTs a presigned direct upload. Absolute http(s) URL or a
5001
+ * site-relative path; defaults to the S3 REST endpoint the AWS SDK returns.
5002
+ *
5003
+ * Set this to route uploads through your own CDN so they are same-origin with the
5004
+ * editor, which removes the need for a bucket CORS rule naming an exact origin. The
5005
+ * signature is unaffected — a presigned POST's string-to-sign is the base64 policy
5006
+ * alone, so the host never enters it (pinned by store-s3.test.ts).
5007
+ *
5008
+ * NOT a prefix. Unlike `publicBaseUrl`, nothing is joined onto this value; it replaces
5009
+ * the POST target outright and is passed through byte-for-byte. Do not route it through
5010
+ * `joinUrlPrefix` "for consistency" — that would strip the trailing slash and silently
5011
+ * stop a CDN path pattern like `/asset-upload/*` from matching.
5012
+ */
5013
+ uploadUrl: uploadTargetUrlSchema2.optional(),
4860
5014
  /** Max upload size in bytes for presigned direct uploads. Defaults to 50 MiB. */
4861
- maxUploadBytes: z42.number().int().positive().optional()
4862
- }),
4863
- z42.object({
4864
- adapter: z42.literal("lfs"),
4865
- publicBaseUrl: z42.string().url().optional()
4866
- })
5015
+ maxUploadBytes: z52.number().int().positive().optional()
5016
+ }).strict(),
5017
+ z52.object({
5018
+ adapter: z52.literal("lfs"),
5019
+ publicBaseUrl: assetMountUrlSchema2.optional()
5020
+ }).strict()
4867
5021
  ]);
4868
5022
  }
4869
5023
  });
@@ -4872,44 +5026,44 @@ var init_generate_ai_content = __esm({
4872
5026
  "use strict";
4873
5027
  init_collection2();
4874
5028
  init_media2();
4875
- defaultBranchAccessSchema2 = z52.enum(["allow", "deny"]).default("deny");
4876
- defaultPathAccessLevelSchema2 = z52.enum(["allow", "deny"]);
4877
- defaultPathAccessLevelsSchema2 = z52.object({
5029
+ defaultBranchAccessSchema2 = z62.enum(["allow", "deny"]).default("deny");
5030
+ defaultPathAccessLevelSchema2 = z62.enum(["allow", "deny"]);
5031
+ defaultPathAccessLevelsSchema2 = z62.object({
4878
5032
  read: defaultPathAccessLevelSchema2.optional(),
4879
5033
  edit: defaultPathAccessLevelSchema2.optional(),
4880
5034
  review: defaultPathAccessLevelSchema2.optional()
4881
5035
  }).strict();
4882
- defaultPathAccessSchema2 = z52.union([defaultPathAccessLevelSchema2, defaultPathAccessLevelsSchema2]).default("deny");
4883
- defaultBaseBranchSchema2 = z52.string().default("main");
4884
- defaultRemoteNameSchema2 = z52.string().default("origin");
4885
- defaultRemoteUrlSchema2 = z52.string().min(1);
4886
- gitBotAuthorNameSchema2 = z52.string().min(1);
4887
- gitBotAuthorEmailSchema2 = z52.string().email();
4888
- githubTokenEnvVarSchema2 = z52.string().default("GITHUB_BOT_TOKEN");
4889
- operatingModeSchema2 = z52.enum(["prod", "dev"]);
4890
- deployedAsSchema2 = z52.enum(["static", "server"]).default("server");
5036
+ defaultPathAccessSchema2 = z62.union([defaultPathAccessLevelSchema2, defaultPathAccessLevelsSchema2]).default("deny");
5037
+ defaultBaseBranchSchema2 = z62.string().default("main");
5038
+ defaultRemoteNameSchema2 = z62.string().default("origin");
5039
+ defaultRemoteUrlSchema2 = z62.string().min(1);
5040
+ gitBotAuthorNameSchema2 = z62.string().min(1);
5041
+ gitBotAuthorEmailSchema2 = z62.string().email();
5042
+ githubTokenEnvVarSchema2 = z62.string().default("GITHUB_BOT_TOKEN");
5043
+ operatingModeSchema2 = z62.enum(["prod", "dev"]);
5044
+ deployedAsSchema2 = z62.enum(["static", "server"]).default("server");
4891
5045
  contentRootSchema2 = relativePathSchema2.default("content");
4892
- sourceRootSchema2 = z52.string().min(1).optional();
4893
- basePathSchema2 = z52.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
5046
+ sourceRootSchema2 = z62.string().min(1).optional();
5047
+ basePathSchema2 = z62.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
4894
5048
  message: "basePath must not be empty or only whitespace"
4895
5049
  }).optional();
4896
- deploymentNameSchema2 = z52.string().default("prod");
4897
- devContentSyncSchema2 = z52.enum(["off", "warn"]).default("warn");
4898
- devConfigSchema2 = z52.object({
5050
+ deploymentNameSchema2 = z62.string().default("prod");
5051
+ devContentSyncSchema2 = z62.enum(["off", "warn"]).default("warn");
5052
+ devConfigSchema2 = z62.object({
4899
5053
  contentSync: devContentSyncSchema2.optional()
4900
5054
  });
4901
- editorConfigSchema2 = z52.object({
4902
- title: z52.string().optional(),
4903
- subtitle: z52.string().optional(),
4904
- theme: z52.unknown().optional(),
4905
- previewBase: z52.record(z52.string()).optional(),
5055
+ editorConfigSchema2 = z62.object({
5056
+ title: z62.string().optional(),
5057
+ subtitle: z62.string().optional(),
5058
+ theme: z62.unknown().optional(),
5059
+ previewBase: z62.record(z62.string()).optional(),
4906
5060
  // UI handler functions (runtime only, don't serialize)
4907
- onAccountClick: z52.function().returns(z52.void()).optional(),
4908
- onLogoutClick: z52.function().returns(z52.void()).optional(),
5061
+ onAccountClick: z62.function().returns(z62.void()).optional(),
5062
+ onLogoutClick: z62.function().returns(z62.void()).optional(),
4909
5063
  // Optional: custom account component (e.g., Clerk's UserButton)
4910
- AccountComponent: z52.custom().optional()
5064
+ AccountComponent: z62.custom().optional()
4911
5065
  });
4912
- CanopyConfigSchema2 = z52.object({
5066
+ CanopyConfigSchema2 = z62.object({
4913
5067
  media: mediaSchema2.optional(),
4914
5068
  // No outer .optional() here: defaultBranchAccessSchema/defaultPathAccessSchema already
4915
5069
  // have .default('deny'), so the field is optional on input but always resolves to
@@ -4925,7 +5079,7 @@ var init_generate_ai_content = __esm({
4925
5079
  // unset must stay undefined after parsing so dev mode can detect the fork
4926
5080
  // point from git HEAD (see resolveBaseBranch in utils/git.ts).
4927
5081
  defaultBaseBranch: defaultBaseBranchSchema2.optional(),
4928
- defaultActiveBranch: z52.string().min(1).optional(),
5082
+ defaultActiveBranch: z62.string().min(1).optional(),
4929
5083
  defaultRemoteName: defaultRemoteNameSchema2.optional(),
4930
5084
  defaultRemoteUrl: defaultRemoteUrlSchema2.optional(),
4931
5085
  gitBotAuthorName: gitBotAuthorNameSchema2,
@@ -4939,9 +5093,9 @@ var init_generate_ai_content = __esm({
4939
5093
  // Escape hatch for prod hosts that genuinely have internet access and intentionally
4940
5094
  // run git against a network remote (see GitManager.resolveRemoteUrl's prod-mode guard).
4941
5095
  // Default false/unset — the standard AWS Lambda+worker topology must leave this unset.
4942
- allowNetworkRemoteInProd: z52.boolean().optional(),
4943
- settingsBranch: z52.string().optional(),
4944
- autoCreateSettingsPR: z52.boolean().optional(),
5096
+ allowNetworkRemoteInProd: z62.boolean().optional(),
5097
+ settingsBranch: z62.string().optional(),
5098
+ autoCreateSettingsPR: z62.boolean().optional(),
4945
5099
  // Deliberately `deploymentNameSchema.optional()`, NOT `deploymentNameSchema` alone.
4946
5100
  // deploymentNameSchema.default('prod') would make `parse(undefined)` resolve to
4947
5101
  // the literal 'prod' instead of staying `undefined` — collapsing the
@@ -4957,9 +5111,9 @@ var init_generate_ai_content = __esm({
4957
5111
  sourceRoot: sourceRootSchema2.optional(),
4958
5112
  basePath: basePathSchema2,
4959
5113
  editor: editorConfigSchema2.optional(),
4960
- authPlugin: z52.custom().optional(),
4961
- entryLinkUrl: z52.custom().optional(),
4962
- validateEntry: z52.custom().optional(),
5114
+ authPlugin: z62.custom().optional(),
5115
+ entryLinkUrl: z62.custom().optional(),
5116
+ validateEntry: z62.custom().optional(),
4963
5117
  dev: devConfigSchema2.optional()
4964
5118
  }).strict();
4965
5119
  DEFAULT_PROD_WORKSPACE2 = "/mnt/efs/workspace";
@@ -5080,12 +5234,12 @@ var init_generate_ai_content = __esm({
5080
5234
  init_permissions2 = __esm3({
5081
5235
  "dist/config/schemas/permissions.js"() {
5082
5236
  "use strict";
5083
- permissionTargetSchema2 = z6.object({
5084
- allowedUsers: z6.array(z6.string()).optional(),
5085
- allowedGroups: z6.array(z6.string()).optional()
5237
+ permissionTargetSchema2 = z7.object({
5238
+ allowedUsers: z7.array(z7.string()).optional(),
5239
+ allowedGroups: z7.array(z7.string()).optional()
5086
5240
  });
5087
- pathPermissionSchema2 = z6.object({
5088
- path: z6.string().min(1),
5241
+ pathPermissionSchema2 = z7.object({
5242
+ path: z7.string().min(1),
5089
5243
  read: permissionTargetSchema2.optional(),
5090
5244
  edit: permissionTargetSchema2.optional(),
5091
5245
  review: permissionTargetSchema2.optional()
@@ -5268,6 +5422,7 @@ var init_generate_ai_content = __esm({
5268
5422
  sleep = (ms) => new Promise((resolve2) => {
5269
5423
  setTimeout(resolve2, ms);
5270
5424
  });
5425
+ init_sanitize_href2();
5271
5426
  DEFAULT_SEO_FIELD_NAMES = {
5272
5427
  title: "metaTitle",
5273
5428
  description: "metaDescription",
@@ -5780,30 +5935,30 @@ var init_generate_ai_content = __esm({
5780
5935
  log2 = createDebugLogger2({ prefix: "ResourceGeneration" });
5781
5936
  META_DIR2 = ".canopy-meta";
5782
5937
  init_logger2();
5783
- entryTypeMetaSchema = z7.object({
5784
- name: z7.string().min(1),
5785
- format: z7.enum(["md", "mdx", "json", "yaml"]),
5786
- schema: z7.string().min(1),
5938
+ entryTypeMetaSchema = z8.object({
5939
+ name: z8.string().min(1),
5940
+ format: z8.enum(["md", "mdx", "json", "yaml"]),
5941
+ schema: z8.string().min(1),
5787
5942
  // Entry schema registry key (validated at resolution time)
5788
- label: z7.string().optional(),
5789
- description: z7.string().optional(),
5790
- default: z7.boolean().optional(),
5791
- maxItems: z7.number().int().positive().optional()
5943
+ label: z8.string().optional(),
5944
+ description: z8.string().optional(),
5945
+ default: z8.boolean().optional(),
5946
+ maxItems: z8.number().int().positive().optional()
5792
5947
  });
5793
- collectionMetaSchema = z7.object({
5794
- name: z7.string().min(1),
5795
- label: z7.string().optional(),
5796
- description: z7.string().optional(),
5797
- entries: z7.array(entryTypeMetaSchema).optional(),
5798
- order: z7.array(z7.string()).default([])
5948
+ collectionMetaSchema = z8.object({
5949
+ name: z8.string().min(1),
5950
+ label: z8.string().optional(),
5951
+ description: z8.string().optional(),
5952
+ entries: z8.array(entryTypeMetaSchema).optional(),
5953
+ order: z8.array(z8.string()).default([])
5799
5954
  // Embedded IDs for ordering; optional in the file, [] = alphabetical
5800
5955
  }).refine((data) => data.entries && data.entries.length > 0, {
5801
5956
  message: "Collection must have at least one entry type"
5802
5957
  });
5803
- rootCollectionMetaSchema = z7.object({
5804
- label: z7.string().optional(),
5805
- entries: z7.array(entryTypeMetaSchema).optional(),
5806
- order: z7.array(z7.string()).optional()
5958
+ rootCollectionMetaSchema = z8.object({
5959
+ label: z8.string().optional(),
5960
+ entries: z8.array(entryTypeMetaSchema).optional(),
5961
+ order: z8.array(z8.string()).optional()
5807
5962
  // Embedded IDs for ordering items
5808
5963
  });
5809
5964
  init_flatten2();
@@ -9102,7 +9257,7 @@ var init_content_id_index = __esm({
9102
9257
  // dist/schema/meta-loader.js
9103
9258
  import { promises as fs21 } from "fs";
9104
9259
  import { join as join4 } from "pathe";
9105
- import { z as z8 } from "zod";
9260
+ import { z as z9 } from "zod";
9106
9261
  import chokidar2 from "chokidar";
9107
9262
  function stripEmbeddedIdFromName2(name) {
9108
9263
  return extractSlugFromFilename2(name);
@@ -9181,30 +9336,30 @@ var init_meta_loader = __esm({
9181
9336
  init_error();
9182
9337
  init_logger3();
9183
9338
  init_content_id_index();
9184
- entryTypeMetaSchema2 = z8.object({
9185
- name: z8.string().min(1),
9186
- format: z8.enum(["md", "mdx", "json", "yaml"]),
9187
- schema: z8.string().min(1),
9339
+ entryTypeMetaSchema2 = z9.object({
9340
+ name: z9.string().min(1),
9341
+ format: z9.enum(["md", "mdx", "json", "yaml"]),
9342
+ schema: z9.string().min(1),
9188
9343
  // Entry schema registry key (validated at resolution time)
9189
- label: z8.string().optional(),
9190
- description: z8.string().optional(),
9191
- default: z8.boolean().optional(),
9192
- maxItems: z8.number().int().positive().optional()
9344
+ label: z9.string().optional(),
9345
+ description: z9.string().optional(),
9346
+ default: z9.boolean().optional(),
9347
+ maxItems: z9.number().int().positive().optional()
9193
9348
  });
9194
- collectionMetaSchema2 = z8.object({
9195
- name: z8.string().min(1),
9196
- label: z8.string().optional(),
9197
- description: z8.string().optional(),
9198
- entries: z8.array(entryTypeMetaSchema2).optional(),
9199
- order: z8.array(z8.string()).default([])
9349
+ collectionMetaSchema2 = z9.object({
9350
+ name: z9.string().min(1),
9351
+ label: z9.string().optional(),
9352
+ description: z9.string().optional(),
9353
+ entries: z9.array(entryTypeMetaSchema2).optional(),
9354
+ order: z9.array(z9.string()).default([])
9200
9355
  // Embedded IDs for ordering; optional in the file, [] = alphabetical
9201
9356
  }).refine((data) => data.entries && data.entries.length > 0, {
9202
9357
  message: "Collection must have at least one entry type"
9203
9358
  });
9204
- rootCollectionMetaSchema2 = z8.object({
9205
- label: z8.string().optional(),
9206
- entries: z8.array(entryTypeMetaSchema2).optional(),
9207
- order: z8.array(z8.string()).optional()
9359
+ rootCollectionMetaSchema2 = z9.object({
9360
+ label: z9.string().optional(),
9361
+ entries: z9.array(entryTypeMetaSchema2).optional(),
9362
+ order: z9.array(z9.string()).optional()
9208
9363
  // Embedded IDs for ordering items
9209
9364
  });
9210
9365
  }
@@ -9262,88 +9417,88 @@ var init_types5 = __esm({
9262
9417
  });
9263
9418
 
9264
9419
  // dist/config/schemas/field.js
9265
- import { z as z9 } from "zod";
9420
+ import { z as z10 } from "zod";
9266
9421
  var fieldBaseSchema3, selectOptionSchema3, referenceOptionSchema3, primitiveFieldSchema3, ASPECT_RATIO_RE3, imageFieldSchema3, selectFieldSchema3, referenceFieldSchema3, fieldHolder3, blockSchema3, blockFieldSchema3, objectFieldSchema3, inlineGroupFieldSchema3, customFieldSchema3, knownFieldSchema3, fieldSchema3;
9267
9422
  var init_field3 = __esm({
9268
9423
  "dist/config/schemas/field.js"() {
9269
9424
  "use strict";
9270
9425
  init_types5();
9271
- fieldBaseSchema3 = z9.object({
9272
- name: z9.string().min(1),
9273
- label: z9.string().optional(),
9274
- description: z9.string().optional(),
9275
- required: z9.boolean().optional(),
9276
- list: z9.boolean().optional(),
9277
- isTitle: z9.boolean().optional(),
9278
- isBody: z9.boolean().optional()
9426
+ fieldBaseSchema3 = z10.object({
9427
+ name: z10.string().min(1),
9428
+ label: z10.string().optional(),
9429
+ description: z10.string().optional(),
9430
+ required: z10.boolean().optional(),
9431
+ list: z10.boolean().optional(),
9432
+ isTitle: z10.boolean().optional(),
9433
+ isBody: z10.boolean().optional()
9279
9434
  });
9280
- selectOptionSchema3 = z9.union([
9281
- z9.string(),
9282
- z9.object({
9283
- label: z9.string().min(1),
9284
- value: z9.string().min(1)
9435
+ selectOptionSchema3 = z10.union([
9436
+ z10.string(),
9437
+ z10.object({
9438
+ label: z10.string().min(1),
9439
+ value: z10.string().min(1)
9285
9440
  })
9286
9441
  ]);
9287
- referenceOptionSchema3 = z9.union([
9288
- z9.string(),
9289
- z9.object({
9290
- label: z9.string().min(1),
9291
- value: z9.string().min(1)
9442
+ referenceOptionSchema3 = z10.union([
9443
+ z10.string(),
9444
+ z10.object({
9445
+ label: z10.string().min(1),
9446
+ value: z10.string().min(1)
9292
9447
  })
9293
9448
  ]);
9294
9449
  primitiveFieldSchema3 = fieldBaseSchema3.extend({
9295
- type: z9.enum(primitiveFieldTypes3)
9450
+ type: z10.enum(primitiveFieldTypes3)
9296
9451
  });
9297
9452
  ASPECT_RATIO_RE3 = /^[1-9][0-9]*:[1-9][0-9]*$/;
9298
9453
  imageFieldSchema3 = fieldBaseSchema3.extend({
9299
- type: z9.literal("image"),
9300
- aspect: z9.string().regex(ASPECT_RATIO_RE3, 'aspect must be "W:H" with positive integers (e.g. "16:9")').optional(),
9301
- altOptional: z9.boolean().optional()
9454
+ type: z10.literal("image"),
9455
+ aspect: z10.string().regex(ASPECT_RATIO_RE3, 'aspect must be "W:H" with positive integers (e.g. "16:9")').optional(),
9456
+ altOptional: z10.boolean().optional()
9302
9457
  });
9303
9458
  selectFieldSchema3 = fieldBaseSchema3.extend({
9304
- type: z9.literal("select"),
9305
- options: z9.array(selectOptionSchema3).min(1)
9459
+ type: z10.literal("select"),
9460
+ options: z10.array(selectOptionSchema3).min(1)
9306
9461
  });
9307
9462
  referenceFieldSchema3 = fieldBaseSchema3.extend({
9308
- type: z9.literal("reference"),
9309
- collections: z9.array(z9.string().min(1)).min(1).optional(),
9310
- entryTypes: z9.array(z9.string().min(1)).min(1).optional(),
9311
- displayField: z9.string().min(1).optional(),
9312
- options: z9.array(referenceOptionSchema3).optional(),
9463
+ type: z10.literal("reference"),
9464
+ collections: z10.array(z10.string().min(1)).min(1).optional(),
9465
+ entryTypes: z10.array(z10.string().min(1)).min(1).optional(),
9466
+ displayField: z10.string().min(1).optional(),
9467
+ options: z10.array(referenceOptionSchema3).optional(),
9313
9468
  // Must be listed here, not just on ReferenceFieldConfig: zod strips unknown keys by
9314
9469
  // default, so a consumer that adopts this schema's parse output would silently delete a
9315
9470
  // runtime-consumed flag and the feature would no-op with no error. (`resolvedSchema` is
9316
9471
  // absent on purpose — it is type-inference-only and stripping it changes no behavior.)
9317
- includeBody: z9.boolean().optional()
9472
+ includeBody: z10.boolean().optional()
9318
9473
  });
9319
- fieldHolder3 = [z9.never()];
9320
- blockSchema3 = z9.object({
9321
- name: z9.string().min(1),
9322
- label: z9.string().optional(),
9323
- description: z9.string().optional(),
9324
- fields: z9.array(z9.lazy(() => fieldHolder3[0])).min(1)
9474
+ fieldHolder3 = [z10.never()];
9475
+ blockSchema3 = z10.object({
9476
+ name: z10.string().min(1),
9477
+ label: z10.string().optional(),
9478
+ description: z10.string().optional(),
9479
+ fields: z10.array(z10.lazy(() => fieldHolder3[0])).min(1)
9325
9480
  });
9326
9481
  blockFieldSchema3 = fieldBaseSchema3.extend({
9327
- type: z9.literal("block"),
9328
- templates: z9.array(blockSchema3).min(1)
9482
+ type: z10.literal("block"),
9483
+ templates: z10.array(blockSchema3).min(1)
9329
9484
  });
9330
9485
  objectFieldSchema3 = fieldBaseSchema3.extend({
9331
- type: z9.literal("object"),
9332
- fields: z9.array(z9.lazy(() => fieldHolder3[0])).min(1)
9486
+ type: z10.literal("object"),
9487
+ fields: z10.array(z10.lazy(() => fieldHolder3[0])).min(1)
9333
9488
  });
9334
- inlineGroupFieldSchema3 = z9.object({
9335
- type: z9.literal("group"),
9336
- name: z9.string().min(1),
9337
- label: z9.string().optional(),
9338
- description: z9.string().optional(),
9339
- fields: z9.array(z9.lazy(() => fieldHolder3[0])).min(1)
9489
+ inlineGroupFieldSchema3 = z10.object({
9490
+ type: z10.literal("group"),
9491
+ name: z10.string().min(1),
9492
+ label: z10.string().optional(),
9493
+ description: z10.string().optional(),
9494
+ fields: z10.array(z10.lazy(() => fieldHolder3[0])).min(1)
9340
9495
  });
9341
- customFieldSchema3 = z9.lazy(() => fieldBaseSchema3.extend({
9342
- type: z9.string().min(1).refine((val) => !fieldTypes3.includes(val), {
9496
+ customFieldSchema3 = z10.lazy(() => fieldBaseSchema3.extend({
9497
+ type: z10.string().min(1).refine((val) => !fieldTypes3.includes(val), {
9343
9498
  message: "Custom field types must not conflict with built-in types"
9344
9499
  })
9345
9500
  }).passthrough());
9346
- knownFieldSchema3 = z9.discriminatedUnion("type", [
9501
+ knownFieldSchema3 = z10.discriminatedUnion("type", [
9347
9502
  primitiveFieldSchema3,
9348
9503
  selectFieldSchema3,
9349
9504
  referenceFieldSchema3,
@@ -9352,39 +9507,39 @@ var init_field3 = __esm({
9352
9507
  blockFieldSchema3,
9353
9508
  inlineGroupFieldSchema3
9354
9509
  ]);
9355
- fieldSchema3 = z9.lazy(() => z9.union([knownFieldSchema3, customFieldSchema3]));
9510
+ fieldSchema3 = z10.lazy(() => z10.union([knownFieldSchema3, customFieldSchema3]));
9356
9511
  fieldHolder3[0] = fieldSchema3;
9357
9512
  }
9358
9513
  });
9359
9514
 
9360
9515
  // dist/config/schemas/collection.js
9361
- import { z as z10 } from "zod";
9516
+ import { z as z11 } from "zod";
9362
9517
  import { isAbsolute as isAbsolute3 } from "pathe";
9363
9518
  var relativePathSchema3, entryTypeSchema3, collectionSchema3;
9364
9519
  var init_collection3 = __esm({
9365
9520
  "dist/config/schemas/collection.js"() {
9366
9521
  "use strict";
9367
9522
  init_field3();
9368
- relativePathSchema3 = z10.string().min(1).refine((val) => !isAbsolute3(val), { message: "Path must be relative" }).refine((val) => !val.split(/[\\/]+/).includes(".."), {
9523
+ relativePathSchema3 = z11.string().min(1).refine((val) => !isAbsolute3(val), { message: "Path must be relative" }).refine((val) => !val.split(/[\\/]+/).includes(".."), {
9369
9524
  message: 'Path must not contain ".."'
9370
9525
  }).transform((val) => val.split(/[\\/]+/).filter(Boolean).join("/"));
9371
- entryTypeSchema3 = z10.object({
9372
- name: z10.string().min(1),
9373
- format: z10.enum(["md", "mdx", "json", "yaml"]),
9374
- schema: z10.array(z10.lazy(() => fieldSchema3)).min(1),
9375
- label: z10.string().optional(),
9376
- description: z10.string().optional(),
9377
- default: z10.boolean().optional(),
9378
- maxItems: z10.number().int().positive().optional()
9526
+ entryTypeSchema3 = z11.object({
9527
+ name: z11.string().min(1),
9528
+ format: z11.enum(["md", "mdx", "json", "yaml"]),
9529
+ schema: z11.array(z11.lazy(() => fieldSchema3)).min(1),
9530
+ label: z11.string().optional(),
9531
+ description: z11.string().optional(),
9532
+ default: z11.boolean().optional(),
9533
+ maxItems: z11.number().int().positive().optional()
9379
9534
  });
9380
- collectionSchema3 = z10.lazy(() => z10.object({
9381
- name: z10.string().min(1),
9535
+ collectionSchema3 = z11.lazy(() => z11.object({
9536
+ name: z11.string().min(1),
9382
9537
  path: relativePathSchema3,
9383
- label: z10.string().optional(),
9384
- description: z10.string().optional(),
9385
- entries: z10.array(entryTypeSchema3).optional(),
9386
- collections: z10.array(collectionSchema3).optional(),
9387
- order: z10.array(z10.string()).optional()
9538
+ label: z11.string().optional(),
9539
+ description: z11.string().optional(),
9540
+ entries: z11.array(entryTypeSchema3).optional(),
9541
+ collections: z11.array(collectionSchema3).optional(),
9542
+ order: z11.array(z11.string()).optional()
9388
9543
  // Embedded IDs for ordering items
9389
9544
  }).refine((data) => data.entries || data.collections, {
9390
9545
  message: "Collection must have entries or collections"
@@ -9392,82 +9547,165 @@ var init_collection3 = __esm({
9392
9547
  }
9393
9548
  });
9394
9549
 
9550
+ // dist/utils/sanitize-href.js
9551
+ function declaresScheme3(url) {
9552
+ return /^[a-z][a-z0-9+.-]*:/i.test(url.trim());
9553
+ }
9554
+ function isImplicitlyOffOrigin3(url) {
9555
+ if (declaresScheme3(url))
9556
+ return false;
9557
+ try {
9558
+ return new URL(url, SENTINEL_BASE3).origin !== SENTINEL_BASE3;
9559
+ } catch {
9560
+ return false;
9561
+ }
9562
+ }
9563
+ function isHttpUrlOrSameOriginPath3(value, opts = {}) {
9564
+ if (value === "")
9565
+ return false;
9566
+ for (let index = 0; index < value.length; index++) {
9567
+ const code = value.charCodeAt(index);
9568
+ if (code <= 32 || code === 127)
9569
+ return false;
9570
+ }
9571
+ if (value.includes("?") || value.includes("#"))
9572
+ return false;
9573
+ if (value.includes("\\"))
9574
+ return false;
9575
+ if (DOT_SEGMENT3.test(value))
9576
+ return false;
9577
+ if (declaresScheme3(value)) {
9578
+ if (!/^https?:\/\//i.test(value))
9579
+ return false;
9580
+ try {
9581
+ new URL(value);
9582
+ return true;
9583
+ } catch {
9584
+ return false;
9585
+ }
9586
+ }
9587
+ if (isImplicitlyOffOrigin3(value)) {
9588
+ return opts.allowProtocolRelative === true && value.startsWith("//") && value[2] !== "/";
9589
+ }
9590
+ return value.startsWith("/") && value[1] !== "/";
9591
+ }
9592
+ var SENTINEL_BASE3, DOT_SEGMENT3;
9593
+ var init_sanitize_href3 = __esm({
9594
+ "dist/utils/sanitize-href.js"() {
9595
+ "use strict";
9596
+ SENTINEL_BASE3 = "https://relative.invalid";
9597
+ DOT_SEGMENT3 = /(^|\/)(\.|%2e){1,2}(\/|$)/i;
9598
+ }
9599
+ });
9600
+
9601
+ // dist/config/schemas/url.js
9602
+ import { z as z12 } from "zod";
9603
+ var uploadTargetUrlSchema3, assetMountUrlSchema3;
9604
+ var init_url3 = __esm({
9605
+ "dist/config/schemas/url.js"() {
9606
+ "use strict";
9607
+ init_sanitize_href3();
9608
+ uploadTargetUrlSchema3 = z12.string().trim().refine((value) => isHttpUrlOrSameOriginPath3(value), {
9609
+ message: 'must be an absolute http(s) URL or a site-relative path beginning with a single "/", with no query or fragment'
9610
+ });
9611
+ assetMountUrlSchema3 = z12.string().trim().refine((value) => isHttpUrlOrSameOriginPath3(value, { allowProtocolRelative: true }), {
9612
+ message: 'must be an absolute http(s) URL, a protocol-relative "//host" URL, or a site-relative path beginning with a single "/", with no query or fragment'
9613
+ });
9614
+ }
9615
+ });
9616
+
9395
9617
  // dist/config/schemas/media.js
9396
- import { z as z11 } from "zod";
9618
+ import { z as z13 } from "zod";
9397
9619
  var mediaSchema3;
9398
9620
  var init_media3 = __esm({
9399
9621
  "dist/config/schemas/media.js"() {
9400
9622
  "use strict";
9401
9623
  init_collection3();
9402
- mediaSchema3 = z11.discriminatedUnion("adapter", [
9403
- z11.object({
9404
- adapter: z11.literal("local"),
9405
- publicBaseUrl: z11.string().url().optional(),
9624
+ init_url3();
9625
+ mediaSchema3 = z13.discriminatedUnion("adapter", [
9626
+ z13.object({
9627
+ adapter: z13.literal("local"),
9628
+ publicBaseUrl: assetMountUrlSchema3.optional(),
9406
9629
  /** Root directory for local asset storage. Defaults to the caller's dev-assets dir. */
9407
9630
  directory: relativePathSchema3.optional()
9408
- }),
9409
- z11.object({
9410
- adapter: z11.literal("s3"),
9411
- bucket: z11.string().min(1),
9412
- region: z11.string().min(1),
9413
- publicBaseUrl: z11.string().url().optional(),
9631
+ }).strict(),
9632
+ z13.object({
9633
+ adapter: z13.literal("s3"),
9634
+ bucket: z13.string().min(1),
9635
+ region: z13.string().min(1),
9636
+ publicBaseUrl: assetMountUrlSchema3.optional(),
9637
+ /**
9638
+ * Where the browser POSTs a presigned direct upload. Absolute http(s) URL or a
9639
+ * site-relative path; defaults to the S3 REST endpoint the AWS SDK returns.
9640
+ *
9641
+ * Set this to route uploads through your own CDN so they are same-origin with the
9642
+ * editor, which removes the need for a bucket CORS rule naming an exact origin. The
9643
+ * signature is unaffected — a presigned POST's string-to-sign is the base64 policy
9644
+ * alone, so the host never enters it (pinned by store-s3.test.ts).
9645
+ *
9646
+ * NOT a prefix. Unlike `publicBaseUrl`, nothing is joined onto this value; it replaces
9647
+ * the POST target outright and is passed through byte-for-byte. Do not route it through
9648
+ * `joinUrlPrefix` "for consistency" — that would strip the trailing slash and silently
9649
+ * stop a CDN path pattern like `/asset-upload/*` from matching.
9650
+ */
9651
+ uploadUrl: uploadTargetUrlSchema3.optional(),
9414
9652
  /** Max upload size in bytes for presigned direct uploads. Defaults to 50 MiB. */
9415
- maxUploadBytes: z11.number().int().positive().optional()
9416
- }),
9417
- z11.object({
9418
- adapter: z11.literal("lfs"),
9419
- publicBaseUrl: z11.string().url().optional()
9420
- })
9653
+ maxUploadBytes: z13.number().int().positive().optional()
9654
+ }).strict(),
9655
+ z13.object({
9656
+ adapter: z13.literal("lfs"),
9657
+ publicBaseUrl: assetMountUrlSchema3.optional()
9658
+ }).strict()
9421
9659
  ]);
9422
9660
  }
9423
9661
  });
9424
9662
 
9425
9663
  // dist/config/schemas/config.js
9426
- import { z as z12 } from "zod";
9664
+ import { z as z14 } from "zod";
9427
9665
  var defaultBranchAccessSchema3, defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3, defaultPathAccessSchema3, defaultBaseBranchSchema3, defaultRemoteNameSchema3, defaultRemoteUrlSchema3, gitBotAuthorNameSchema3, gitBotAuthorEmailSchema3, githubTokenEnvVarSchema3, operatingModeSchema3, deployedAsSchema3, contentRootSchema3, sourceRootSchema3, basePathSchema3, deploymentNameSchema3, devContentSyncSchema3, devConfigSchema3, editorConfigSchema3, CanopyConfigSchema3, DEFAULT_PROD_WORKSPACE3;
9428
9666
  var init_config4 = __esm({
9429
9667
  "dist/config/schemas/config.js"() {
9430
9668
  "use strict";
9431
9669
  init_collection3();
9432
9670
  init_media3();
9433
- defaultBranchAccessSchema3 = z12.enum(["allow", "deny"]).default("deny");
9434
- defaultPathAccessLevelSchema3 = z12.enum(["allow", "deny"]);
9435
- defaultPathAccessLevelsSchema3 = z12.object({
9671
+ defaultBranchAccessSchema3 = z14.enum(["allow", "deny"]).default("deny");
9672
+ defaultPathAccessLevelSchema3 = z14.enum(["allow", "deny"]);
9673
+ defaultPathAccessLevelsSchema3 = z14.object({
9436
9674
  read: defaultPathAccessLevelSchema3.optional(),
9437
9675
  edit: defaultPathAccessLevelSchema3.optional(),
9438
9676
  review: defaultPathAccessLevelSchema3.optional()
9439
9677
  }).strict();
9440
- defaultPathAccessSchema3 = z12.union([defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3]).default("deny");
9441
- defaultBaseBranchSchema3 = z12.string().default("main");
9442
- defaultRemoteNameSchema3 = z12.string().default("origin");
9443
- defaultRemoteUrlSchema3 = z12.string().min(1);
9444
- gitBotAuthorNameSchema3 = z12.string().min(1);
9445
- gitBotAuthorEmailSchema3 = z12.string().email();
9446
- githubTokenEnvVarSchema3 = z12.string().default("GITHUB_BOT_TOKEN");
9447
- operatingModeSchema3 = z12.enum(["prod", "dev"]);
9448
- deployedAsSchema3 = z12.enum(["static", "server"]).default("server");
9678
+ defaultPathAccessSchema3 = z14.union([defaultPathAccessLevelSchema3, defaultPathAccessLevelsSchema3]).default("deny");
9679
+ defaultBaseBranchSchema3 = z14.string().default("main");
9680
+ defaultRemoteNameSchema3 = z14.string().default("origin");
9681
+ defaultRemoteUrlSchema3 = z14.string().min(1);
9682
+ gitBotAuthorNameSchema3 = z14.string().min(1);
9683
+ gitBotAuthorEmailSchema3 = z14.string().email();
9684
+ githubTokenEnvVarSchema3 = z14.string().default("GITHUB_BOT_TOKEN");
9685
+ operatingModeSchema3 = z14.enum(["prod", "dev"]);
9686
+ deployedAsSchema3 = z14.enum(["static", "server"]).default("server");
9449
9687
  contentRootSchema3 = relativePathSchema3.default("content");
9450
- sourceRootSchema3 = z12.string().min(1).optional();
9451
- basePathSchema3 = z12.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
9688
+ sourceRootSchema3 = z14.string().min(1).optional();
9689
+ basePathSchema3 = z14.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
9452
9690
  message: "basePath must not be empty or only whitespace"
9453
9691
  }).optional();
9454
- deploymentNameSchema3 = z12.string().default("prod");
9455
- devContentSyncSchema3 = z12.enum(["off", "warn"]).default("warn");
9456
- devConfigSchema3 = z12.object({
9692
+ deploymentNameSchema3 = z14.string().default("prod");
9693
+ devContentSyncSchema3 = z14.enum(["off", "warn"]).default("warn");
9694
+ devConfigSchema3 = z14.object({
9457
9695
  contentSync: devContentSyncSchema3.optional()
9458
9696
  });
9459
- editorConfigSchema3 = z12.object({
9460
- title: z12.string().optional(),
9461
- subtitle: z12.string().optional(),
9462
- theme: z12.unknown().optional(),
9463
- previewBase: z12.record(z12.string()).optional(),
9697
+ editorConfigSchema3 = z14.object({
9698
+ title: z14.string().optional(),
9699
+ subtitle: z14.string().optional(),
9700
+ theme: z14.unknown().optional(),
9701
+ previewBase: z14.record(z14.string()).optional(),
9464
9702
  // UI handler functions (runtime only, don't serialize)
9465
- onAccountClick: z12.function().returns(z12.void()).optional(),
9466
- onLogoutClick: z12.function().returns(z12.void()).optional(),
9703
+ onAccountClick: z14.function().returns(z14.void()).optional(),
9704
+ onLogoutClick: z14.function().returns(z14.void()).optional(),
9467
9705
  // Optional: custom account component (e.g., Clerk's UserButton)
9468
- AccountComponent: z12.custom().optional()
9706
+ AccountComponent: z14.custom().optional()
9469
9707
  });
9470
- CanopyConfigSchema3 = z12.object({
9708
+ CanopyConfigSchema3 = z14.object({
9471
9709
  media: mediaSchema3.optional(),
9472
9710
  // No outer .optional() here: defaultBranchAccessSchema/defaultPathAccessSchema already
9473
9711
  // have .default('deny'), so the field is optional on input but always resolves to
@@ -9483,7 +9721,7 @@ var init_config4 = __esm({
9483
9721
  // unset must stay undefined after parsing so dev mode can detect the fork
9484
9722
  // point from git HEAD (see resolveBaseBranch in utils/git.ts).
9485
9723
  defaultBaseBranch: defaultBaseBranchSchema3.optional(),
9486
- defaultActiveBranch: z12.string().min(1).optional(),
9724
+ defaultActiveBranch: z14.string().min(1).optional(),
9487
9725
  defaultRemoteName: defaultRemoteNameSchema3.optional(),
9488
9726
  defaultRemoteUrl: defaultRemoteUrlSchema3.optional(),
9489
9727
  gitBotAuthorName: gitBotAuthorNameSchema3,
@@ -9497,9 +9735,9 @@ var init_config4 = __esm({
9497
9735
  // Escape hatch for prod hosts that genuinely have internet access and intentionally
9498
9736
  // run git against a network remote (see GitManager.resolveRemoteUrl's prod-mode guard).
9499
9737
  // Default false/unset — the standard AWS Lambda+worker topology must leave this unset.
9500
- allowNetworkRemoteInProd: z12.boolean().optional(),
9501
- settingsBranch: z12.string().optional(),
9502
- autoCreateSettingsPR: z12.boolean().optional(),
9738
+ allowNetworkRemoteInProd: z14.boolean().optional(),
9739
+ settingsBranch: z14.string().optional(),
9740
+ autoCreateSettingsPR: z14.boolean().optional(),
9503
9741
  // Deliberately `deploymentNameSchema.optional()`, NOT `deploymentNameSchema` alone.
9504
9742
  // deploymentNameSchema.default('prod') would make `parse(undefined)` resolve to
9505
9743
  // the literal 'prod' instead of staying `undefined` — collapsing the
@@ -9515,9 +9753,9 @@ var init_config4 = __esm({
9515
9753
  sourceRoot: sourceRootSchema3.optional(),
9516
9754
  basePath: basePathSchema3,
9517
9755
  editor: editorConfigSchema3.optional(),
9518
- authPlugin: z12.custom().optional(),
9519
- entryLinkUrl: z12.custom().optional(),
9520
- validateEntry: z12.custom().optional(),
9756
+ authPlugin: z14.custom().optional(),
9757
+ entryLinkUrl: z14.custom().optional(),
9758
+ validateEntry: z14.custom().optional(),
9521
9759
  dev: devConfigSchema3.optional()
9522
9760
  }).strict();
9523
9761
  DEFAULT_PROD_WORKSPACE3 = "/mnt/efs/workspace";
@@ -9865,17 +10103,17 @@ var init_deployment_name2 = __esm({
9865
10103
  });
9866
10104
 
9867
10105
  // dist/config/schemas/permissions.js
9868
- import { z as z13 } from "zod";
10106
+ import { z as z15 } from "zod";
9869
10107
  var permissionTargetSchema3, pathPermissionSchema3;
9870
10108
  var init_permissions3 = __esm({
9871
10109
  "dist/config/schemas/permissions.js"() {
9872
10110
  "use strict";
9873
- permissionTargetSchema3 = z13.object({
9874
- allowedUsers: z13.array(z13.string()).optional(),
9875
- allowedGroups: z13.array(z13.string()).optional()
10111
+ permissionTargetSchema3 = z15.object({
10112
+ allowedUsers: z15.array(z15.string()).optional(),
10113
+ allowedGroups: z15.array(z15.string()).optional()
9876
10114
  });
9877
- pathPermissionSchema3 = z13.object({
9878
- path: z13.string().min(1),
10115
+ pathPermissionSchema3 = z15.object({
10116
+ path: z15.string().min(1),
9879
10117
  read: permissionTargetSchema3.optional(),
9880
10118
  edit: permissionTargetSchema3.optional(),
9881
10119
  review: permissionTargetSchema3.optional()