@roottale/cms-core 0.71.0 → 0.72.1

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/index.cjs CHANGED
@@ -69,11 +69,15 @@ __export(src_exports, {
69
69
  SITE_PATTERN_KEY_PATTERN: () => SITE_PATTERN_KEY_PATTERN,
70
70
  SITE_PATTERN_PLACEMENTS_KEY: () => SITE_PATTERN_PLACEMENTS_KEY,
71
71
  SITE_PATTERN_SLOTS: () => SITE_PATTERN_SLOTS,
72
+ VISUAL_EDIT_ATTRIBUTE: () => VISUAL_EDIT_ATTRIBUTE,
73
+ VISUAL_EDIT_PROTOCOL: () => VISUAL_EDIT_PROTOCOL,
74
+ VISUAL_EDIT_PROTOCOL_VERSION: () => VISUAL_EDIT_PROTOCOL_VERSION,
72
75
  applyBlockBindings: () => applyBlockBindings,
73
76
  bindingSrcValue: () => bindingSrcValue,
74
77
  bindingTextValue: () => bindingTextValue,
75
78
  blockSchema: () => blockSchema,
76
79
  boundFieldValue: () => boundFieldValue,
80
+ buildVisualEditPreviewUrl: () => buildVisualEditPreviewUrl,
77
81
  chainResolvers: () => chainResolvers,
78
82
  cmsFontSize: () => cmsFontSize,
79
83
  cmsImageAlign: () => cmsImageAlign,
@@ -99,6 +103,7 @@ __export(src_exports, {
99
103
  findSitePatternPlacementRule: () => findSitePatternPlacementRule,
100
104
  formatMetadataDefaults: () => formatMetadataDefaults,
101
105
  formatSectionLockViolations: () => formatSectionLockViolations,
106
+ getContentValue: () => getContentValue,
102
107
  htmlToPlainText: () => htmlToPlainText,
103
108
  injectStableIdsTree: () => injectStableIdsTree,
104
109
  internalContentKeyFromPath: () => internalContentKeyFromPath,
@@ -113,6 +118,7 @@ __export(src_exports, {
113
118
  isSitePatternSlotKey: () => isSitePatternSlotKey,
114
119
  isValidBlock: () => isValidBlock,
115
120
  isValidSitePatternKey: () => isValidSitePatternKey,
121
+ isVisualEditOrigin: () => isVisualEditOrigin,
116
122
  issuePreviewToken: () => issuePreviewToken,
117
123
  matchesContentScalarCheck: () => matchesContentScalarCheck,
118
124
  mergeMetadataPatch: () => mergeMetadataPatch,
@@ -126,9 +132,12 @@ __export(src_exports, {
126
132
  normalizeRedirectSource: () => normalizeRedirectSource,
127
133
  normalizeRedirectTarget: () => normalizeRedirectTarget,
128
134
  parseBlock: () => parseBlock,
135
+ parseVisualEditAddress: () => parseVisualEditAddress,
136
+ parseVisualEditMessage: () => parseVisualEditMessage,
129
137
  postsForCollection: () => postsForCollection,
130
138
  postsInAnyCollection: () => postsInAnyCollection,
131
139
  postsNotInAnyCollection: () => postsNotInAnyCollection,
140
+ profileQualificationJsonLdProperties: () => profileQualificationJsonLdProperties,
132
141
  readMetadataValues: () => readMetadataValues,
133
142
  readSitePatternPlacements: () => readSitePatternPlacements,
134
143
  readSitePatternPresentation: () => readSitePatternPresentation,
@@ -141,6 +150,8 @@ __export(src_exports, {
141
150
  resolveCategoryTreeEntryPath: () => resolveCategoryTreeEntryPath,
142
151
  resolveCategoryTreeHubPaths: () => resolveCategoryTreeHubPaths,
143
152
  resolveCategoryTreeSelection: () => resolveCategoryTreeSelection,
153
+ resolveContentCta: () => resolveContentCta,
154
+ resolveContentDependencyPaths: () => resolveContentDependencyPaths,
144
155
  resolveMetadata: () => resolveMetadata,
145
156
  resolvePatternSlots: () => resolvePatternSlots,
146
157
  resolvePostBodySource: () => resolvePostBodySource,
@@ -149,17 +160,28 @@ __export(src_exports, {
149
160
  resolvePostPath: () => resolvePostPath,
150
161
  resolvePostPublicPath: () => resolvePostPublicPath,
151
162
  resolvePostUrl: () => resolvePostUrl,
163
+ sameVisualDependencies: () => sameVisualDependencies,
164
+ sameVisualPreviewTarget: () => sameVisualPreviewTarget,
152
165
  sanitizeHtml: () => sanitizeHtml,
153
166
  sanitizeImportedCss: () => sanitizeImportedCss,
154
167
  sanitizeImportedHtml: () => sanitizeImportedHtml,
155
168
  scopeImportedCss: () => scopeImportedCss,
156
169
  selectContentQuery: () => selectContentQuery,
170
+ selectRelatedContent: () => selectRelatedContent,
171
+ selectVisibleFaqEntries: () => selectVisibleFaqEntries,
157
172
  serializeBlock: () => serializeBlock,
173
+ setContentValue: () => setContentValue,
158
174
  setSitePatternPlacementRule: () => setSitePatternPlacementRule,
159
175
  sitePatternButtonColor: () => sitePatternButtonColor,
160
176
  sitePatternPresentationAttributes: () => sitePatternPresentationAttributes,
161
177
  validateCollections: () => validateCollections,
162
- verifyPreviewToken: () => verifyPreviewToken
178
+ verifyPreviewToken: () => verifyPreviewToken,
179
+ visibleFaqPageJsonLd: () => visibleFaqPageJsonLd,
180
+ visualEditAddressKey: () => visualEditAddressKey,
181
+ visualEditAddressSchema: () => visualEditAddressSchema,
182
+ visualEditAttributes: () => visualEditAttributes,
183
+ visualPreviewTargetSchema: () => visualPreviewTargetSchema,
184
+ visualRenderReceiptSchema: () => visualRenderReceiptSchema
163
185
  });
164
186
  module.exports = __toCommonJS(src_exports);
165
187
 
@@ -1338,6 +1360,109 @@ function verifyPreviewToken(secret, token) {
1338
1360
  return { postId, expiresAt: new Date(exp) };
1339
1361
  }
1340
1362
 
1363
+ // src/visual-edit-contract.ts
1364
+ var import_zod2 = require("zod");
1365
+ var VISUAL_EDIT_PROTOCOL = "roottale:visual-edit";
1366
+ var VISUAL_EDIT_PROTOCOL_VERSION = 1;
1367
+ var VISUAL_EDIT_ATTRIBUTE = "data-rt-edit";
1368
+ var identifier = import_zod2.z.string().min(1).max(160).refine((value) => !/[\u0000-\u001f]/.test(value));
1369
+ var safeKey = identifier.refine((value) => !["__proto__", "prototype", "constructor"].includes(value));
1370
+ var pathKey = safeKey.refine((value) => !/^\d+$/.test(value), "\uBC18\uBCF5 \uD56D\uBAA9\uC740 \uC21C\uBC88 \uB300\uC2E0 itemId\uB85C \uC120\uD0DD\uD569\uB2C8\uB2E4.");
1371
+ var version = import_zod2.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
1372
+ var session = import_zod2.z.string().uuid();
1373
+ var position = import_zod2.z.object({ left: import_zod2.z.number().finite().min(0).max(1e7), top: import_zod2.z.number().finite().min(0).max(1e7) }).strict();
1374
+ var pathname = import_zod2.z.string().min(1).max(2e3).refine((value) => value.startsWith("/") && !value.startsWith("//") && !/[\\?#\u0000-\u001f]/.test(value));
1375
+ var visualEditAddressSchema = import_zod2.z.object({
1376
+ entityId: identifier,
1377
+ fieldKey: safeKey,
1378
+ itemId: identifier.optional(),
1379
+ path: import_zod2.z.array(pathKey).max(16).optional()
1380
+ }).strict().refine((address) => {
1381
+ const selector = [...address.path ?? []].reverse().find((part) => part.startsWith("@"));
1382
+ return !selector || !address.itemId || selector.slice(1) === address.itemId;
1383
+ }, "itemId\uB294 \uACBD\uB85C\uC758 \uB9C8\uC9C0\uB9C9 \uBC18\uBCF5 \uD56D\uBAA9 ID\uC640 \uAC19\uC544\uC57C \uD569\uB2C8\uB2E4.");
1384
+ var visualPreviewTargetSchema = import_zod2.z.object({
1385
+ entityId: identifier,
1386
+ draftId: identifier,
1387
+ draftVersion: version,
1388
+ contractVersion: identifier,
1389
+ templateVersion: identifier
1390
+ }).strict();
1391
+ var visualRenderReceiptSchema = visualPreviewTargetSchema.extend({
1392
+ dependencyVersions: import_zod2.z.record(safeKey, identifier).refine((value) => Object.keys(value).length <= 256)
1393
+ }).strict();
1394
+ var envelope = {
1395
+ protocol: import_zod2.z.literal(VISUAL_EDIT_PROTOCOL),
1396
+ protocolVersion: import_zod2.z.literal(VISUAL_EDIT_PROTOCOL_VERSION),
1397
+ session,
1398
+ sequence: import_zod2.z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
1399
+ target: visualPreviewTargetSchema
1400
+ };
1401
+ var messageSchema = import_zod2.z.discriminatedUnion("type", [
1402
+ import_zod2.z.object({ ...envelope, type: import_zod2.z.literal("connect") }).strict(),
1403
+ import_zod2.z.object({ ...envelope, type: import_zod2.z.literal("ready") }).strict(),
1404
+ import_zod2.z.object({ ...envelope, type: import_zod2.z.literal("select"), address: visualEditAddressSchema, position: position.optional() }).strict(),
1405
+ import_zod2.z.object({ ...envelope, type: import_zod2.z.literal("focus"), address: visualEditAddressSchema.optional(), position: position.optional() }).strict(),
1406
+ import_zod2.z.object({ ...envelope, type: import_zod2.z.literal("navigate"), pathname }).strict(),
1407
+ import_zod2.z.object({ ...envelope, type: import_zod2.z.literal("mode"), mode: import_zod2.z.enum(["select", "navigate"]) }).strict(),
1408
+ import_zod2.z.object({ ...envelope, type: import_zod2.z.literal("refresh") }).strict(),
1409
+ import_zod2.z.object({ ...envelope, type: import_zod2.z.literal("viewport"), position }).strict(),
1410
+ import_zod2.z.object({ ...envelope, type: import_zod2.z.literal("rendered"), receipt: visualRenderReceiptSchema }).strict(),
1411
+ import_zod2.z.object({ ...envelope, type: import_zod2.z.literal("error"), code: import_zod2.z.enum(["unavailable", "superseded", "unsupported_contract", "expired"]), message: import_zod2.z.string().max(500).optional() }).strict()
1412
+ ]);
1413
+ function parseVisualEditAddress(value) {
1414
+ const result = visualEditAddressSchema.safeParse(value);
1415
+ return result.success ? result.data : null;
1416
+ }
1417
+ function parseVisualEditMessage(value) {
1418
+ try {
1419
+ if (JSON.stringify(value).length > 64e3) return null;
1420
+ const result = messageSchema.safeParse(value);
1421
+ if (!result.success) return null;
1422
+ const message = result.data;
1423
+ if (message.type === "rendered" && !sameVisualPreviewTarget(message.target, message.receipt)) return null;
1424
+ if (message.type === "focus" && message.address && message.address.entityId !== message.target.entityId) return null;
1425
+ return message;
1426
+ } catch {
1427
+ return null;
1428
+ }
1429
+ }
1430
+ function sameVisualPreviewTarget(a, b) {
1431
+ return a.entityId === b.entityId && a.draftId === b.draftId && a.draftVersion === b.draftVersion && a.contractVersion === b.contractVersion && a.templateVersion === b.templateVersion;
1432
+ }
1433
+ function sameVisualDependencies(a, b) {
1434
+ const keys = Object.keys(a);
1435
+ return keys.length === Object.keys(b).length && keys.every((key) => Object.hasOwn(b, key) && a[key] === b[key]);
1436
+ }
1437
+ function visualEditAddressKey(address) {
1438
+ const path = address.path ?? [];
1439
+ const itemId = [...path].reverse().find((part) => part.startsWith("@"))?.slice(1) ?? address.itemId ?? null;
1440
+ return JSON.stringify([address.entityId, address.fieldKey, itemId, path]);
1441
+ }
1442
+ function visualEditAttributes(address, enabled = true) {
1443
+ const parsed = enabled ? parseVisualEditAddress(address) : null;
1444
+ return parsed ? { [VISUAL_EDIT_ATTRIBUTE]: JSON.stringify(parsed) } : {};
1445
+ }
1446
+ function isVisualEditOrigin(value) {
1447
+ try {
1448
+ const url = new URL(value);
1449
+ const local = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
1450
+ return value === url.origin && (url.protocol === "https:" || local && url.protocol === "http:");
1451
+ } catch {
1452
+ return false;
1453
+ }
1454
+ }
1455
+ function buildVisualEditPreviewUrl(urlValue, editorOrigin, sessionId) {
1456
+ try {
1457
+ const url = new URL(urlValue);
1458
+ if (url.username || url.password || !isVisualEditOrigin(url.origin) || !isVisualEditOrigin(editorOrigin) || !session.safeParse(sessionId).success) return null;
1459
+ url.hash = new URLSearchParams({ "rt-visual-edit": sessionId, origin: editorOrigin }).toString();
1460
+ return url.href;
1461
+ } catch {
1462
+ return null;
1463
+ }
1464
+ }
1465
+
1341
1466
  // src/sanitize.ts
1342
1467
  var import_sanitize_html2 = __toESM(require("sanitize-html"), 1);
1343
1468
  var URL_ATTRS = /* @__PURE__ */ new Set(["href", "src", "xlink:href"]);
@@ -2088,11 +2213,11 @@ function resolveMetadata(input) {
2088
2213
  }
2089
2214
 
2090
2215
  // src/metadata-contract.ts
2091
- var import_zod2 = require("zod");
2216
+ var import_zod3 = require("zod");
2092
2217
  var METADATA_FIELDS = ["title", "description", "ogImage", "ogImageAlt", "noindex", "nofollow"];
2093
- var MetadataFieldSchema = import_zod2.z.enum(METADATA_FIELDS);
2094
- var MetadataTargetKindSchema = import_zod2.z.enum(["route", "post", "term"]);
2095
- var textValue = (max) => import_zod2.z.string().trim().min(1).max(max);
2218
+ var MetadataFieldSchema = import_zod3.z.enum(METADATA_FIELDS);
2219
+ var MetadataTargetKindSchema = import_zod3.z.enum(["route", "post", "term"]);
2220
+ var textValue = (max) => import_zod3.z.string().trim().min(1).max(max);
2096
2221
  var imageValue = textValue(2048).refine((value) => {
2097
2222
  if (/[\\\u0000-\u0020\u007f]/u.test(value) || value.startsWith("//")) return false;
2098
2223
  if (value.startsWith("/")) return true;
@@ -2110,11 +2235,11 @@ var metadataFields = {
2110
2235
  ogImage: imageValue,
2111
2236
  // Automatic alt text may use the complete automatic title and its site suffix.
2112
2237
  ogImageAlt: textValue(1e3),
2113
- noindex: import_zod2.z.boolean(),
2114
- nofollow: import_zod2.z.boolean()
2238
+ noindex: import_zod3.z.boolean(),
2239
+ nofollow: import_zod3.z.boolean()
2115
2240
  };
2116
- var MetadataValuesSchema = import_zod2.z.object(metadataFields).partial().strict();
2117
- var MetadataPatchSchema = import_zod2.z.object({
2241
+ var MetadataValuesSchema = import_zod3.z.object(metadataFields).partial().strict();
2242
+ var MetadataPatchSchema = import_zod3.z.object({
2118
2243
  title: textValue(300).nullable().optional(),
2119
2244
  description: metadataFields.description.nullable().optional(),
2120
2245
  ogImage: metadataFields.ogImage.nullable().optional(),
@@ -2122,11 +2247,11 @@ var MetadataPatchSchema = import_zod2.z.object({
2122
2247
  noindex: metadataFields.noindex.nullable().optional(),
2123
2248
  nofollow: metadataFields.nofollow.nullable().optional()
2124
2249
  }).strict();
2125
- var MetadataRobotsPolicySchema = import_zod2.z.object({
2126
- forceNoindex: import_zod2.z.boolean().optional(),
2127
- forceNofollow: import_zod2.z.boolean().optional(),
2250
+ var MetadataRobotsPolicySchema = import_zod3.z.object({
2251
+ forceNoindex: import_zod3.z.boolean().optional(),
2252
+ forceNofollow: import_zod3.z.boolean().optional(),
2128
2253
  /** Stable policy reason codes. They explain an enforced exclusion, never relax it. */
2129
- noindexReasons: import_zod2.z.array(import_zod2.z.string().trim().min(1).max(200)).max(64).optional()
2254
+ noindexReasons: import_zod3.z.array(import_zod3.z.string().trim().min(1).max(200)).max(64).optional()
2130
2255
  }).strict();
2131
2256
  function readMetadataValues(input) {
2132
2257
  if (!input || typeof input !== "object" || Array.isArray(input)) return {};
@@ -2149,35 +2274,35 @@ function mergeMetadataPatch(current, input) {
2149
2274
  }
2150
2275
 
2151
2276
  // src/metadata-defaults.ts
2152
- var import_zod6 = require("zod");
2277
+ var import_zod7 = require("zod");
2153
2278
 
2154
2279
  // src/metadata-text.ts
2155
- var import_zod3 = require("zod");
2156
- var fallbackText = import_zod3.z.string().max(2e3);
2280
+ var import_zod4 = require("zod");
2281
+ var fallbackText = import_zod4.z.string().max(2e3);
2157
2282
  var templateToken = /\{\{(?:title|field:[a-zA-Z][a-zA-Z0-9_-]{0,159})\}\}/gu;
2158
2283
  var template = fallbackText.refine(
2159
2284
  (value) => !/[{}]/u.test(value.replace(templateToken, "")),
2160
2285
  "\uC790\uB3D9 \uD14D\uC2A4\uD2B8\uC5D0\uB294 {{title}} \uB610\uB294 {{field:\uD544\uB4DC\uC774\uB984}} \uD1A0\uD070\uB9CC \uC0AC\uC6A9\uD569\uB2C8\uB2E4"
2161
2286
  );
2162
- var MetadataTextRuleSchema = import_zod3.z.object({
2163
- source: import_zod3.z.enum(["content", "fallback", "template"]).optional(),
2287
+ var MetadataTextRuleSchema = import_zod4.z.object({
2288
+ source: import_zod4.z.enum(["content", "fallback", "template"]).optional(),
2164
2289
  template: template.optional(),
2165
- stripLeadingLabel: import_zod3.z.enum(["question", "answer"]).optional(),
2166
- replaceLineBreakTokens: import_zod3.z.boolean().optional(),
2167
- removeEmDashes: import_zod3.z.boolean().optional(),
2168
- normalizeWhitespace: import_zod3.z.boolean().optional(),
2169
- maxLength: import_zod3.z.number().int().min(1).max(2e3).optional(),
2170
- lengthUnit: import_zod3.z.enum(["code-units", "code-points"]).optional(),
2171
- wordBoundaryMinLength: import_zod3.z.number().int().min(1).max(2e3).optional(),
2172
- ellipsis: import_zod3.z.string().max(10).optional(),
2173
- bodyFallback: import_zod3.z.object({
2174
- preferContaining: import_zod3.z.string().trim().min(1).max(100).optional(),
2175
- stripLeadingLabel: import_zod3.z.literal("conclusion").optional()
2290
+ stripLeadingLabel: import_zod4.z.enum(["question", "answer"]).optional(),
2291
+ replaceLineBreakTokens: import_zod4.z.boolean().optional(),
2292
+ removeEmDashes: import_zod4.z.boolean().optional(),
2293
+ normalizeWhitespace: import_zod4.z.boolean().optional(),
2294
+ maxLength: import_zod4.z.number().int().min(1).max(2e3).optional(),
2295
+ lengthUnit: import_zod4.z.enum(["code-units", "code-points"]).optional(),
2296
+ wordBoundaryMinLength: import_zod4.z.number().int().min(1).max(2e3).optional(),
2297
+ ellipsis: import_zod4.z.string().max(10).optional(),
2298
+ bodyFallback: import_zod4.z.object({
2299
+ preferContaining: import_zod4.z.string().trim().min(1).max(100).optional(),
2300
+ stripLeadingLabel: import_zod4.z.literal("conclusion").optional()
2176
2301
  }).strict().optional(),
2177
2302
  fallback: fallbackText.optional(),
2178
2303
  fallbackWithParent: fallbackText.optional(),
2179
2304
  fallbackWithoutParent: fallbackText.optional(),
2180
- fallbackBySlug: import_zod3.z.record(import_zod3.z.string().max(200), fallbackText).optional()
2305
+ fallbackBySlug: import_zod4.z.record(import_zod4.z.string().max(200), fallbackText).optional()
2181
2306
  }).strict().refine((rule) => !rule.ellipsis || rule.maxLength !== void 0 && rule.ellipsis.length < rule.maxLength, "\uB9D0\uC904\uC784\uD45C\uC5D0\uB294 \uB354 \uAE34 \uCD5C\uB300 \uAE38\uC774\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4").refine((rule) => rule.source === "template" ? rule.template !== void 0 : rule.template === void 0, "\uD14D\uC2A4\uD2B8 template\uC740 source: template\uACFC \uD568\uAED8 \uC120\uC5B8\uD569\uB2C8\uB2E4").refine((rule) => !rule.wordBoundaryMinLength || Boolean(rule.maxLength && rule.wordBoundaryMinLength < rule.maxLength), "\uB2E8\uC5B4 \uACBD\uACC4\uC5D0\uB294 \uB354 \uAE34 \uCD5C\uB300 \uAE38\uC774\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4");
2182
2307
  function cleanMetadataText(value, rule) {
2183
2308
  let text3 = value.trim();
@@ -2247,29 +2372,29 @@ function formatMetadataText(value, rule, input, name) {
2247
2372
  }
2248
2373
 
2249
2374
  // src/content-policy-schema.ts
2250
- var import_zod4 = require("zod");
2251
- var ContentPolicyFieldNameSchema = import_zod4.z.string().min(1).max(64).regex(/^[A-Za-z_][A-Za-z0-9_]*$/).refine((name) => name !== "prototype" && !Object.hasOwn(Object.prototype, name));
2252
- var ContentPolicyScalarSchema = import_zod4.z.union([
2253
- import_zod4.z.string().max(2048),
2254
- import_zod4.z.number().finite(),
2255
- import_zod4.z.boolean(),
2256
- import_zod4.z.null()
2375
+ var import_zod5 = require("zod");
2376
+ var ContentPolicyFieldNameSchema = import_zod5.z.string().min(1).max(64).regex(/^[A-Za-z_][A-Za-z0-9_]*$/).refine((name) => name !== "prototype" && !Object.hasOwn(Object.prototype, name));
2377
+ var ContentPolicyScalarSchema = import_zod5.z.union([
2378
+ import_zod5.z.string().max(2048),
2379
+ import_zod5.z.number().finite(),
2380
+ import_zod5.z.boolean(),
2381
+ import_zod5.z.null()
2257
2382
  ]);
2258
- var contentPolicyEquality = import_zod4.z.object({
2383
+ var contentPolicyEquality = import_zod5.z.object({
2259
2384
  field: ContentPolicyFieldNameSchema,
2260
2385
  equals: ContentPolicyScalarSchema
2261
2386
  }).strict();
2262
- var ContentPolicyWhenSchema = import_zod4.z.union([
2387
+ var ContentPolicyWhenSchema = import_zod5.z.union([
2263
2388
  contentPolicyEquality,
2264
- import_zod4.z.object({
2265
- all: import_zod4.z.array(contentPolicyEquality).min(1).max(4).optional(),
2266
- unlessAll: import_zod4.z.array(contentPolicyEquality).min(1).max(4).optional()
2389
+ import_zod5.z.object({
2390
+ all: import_zod5.z.array(contentPolicyEquality).min(1).max(4).optional(),
2391
+ unlessAll: import_zod5.z.array(contentPolicyEquality).min(1).max(4).optional()
2267
2392
  }).strict().refine(
2268
2393
  (when2) => when2.all !== void 0 || when2.unlessAll !== void 0,
2269
2394
  "\uCF58\uD150\uCE20 \uC870\uAC74\uC5D0\uB294 all \uB610\uB294 unlessAll \uADF8\uB8F9\uC774 \uD544\uC694\uD569\uB2C8\uB2E4"
2270
2395
  )
2271
2396
  ]);
2272
- var ContentPolicyInternalPathSchema = import_zod4.z.string().min(1).max(2048).refine((path) => {
2397
+ var ContentPolicyInternalPathSchema = import_zod5.z.string().min(1).max(2048).refine((path) => {
2273
2398
  if (!path.startsWith("/") || path.startsWith("//") || /[\\?#\u0000-\u0020\u007f]/u.test(path)) return false;
2274
2399
  try {
2275
2400
  return path.split("/").every((part, index) => {
@@ -2283,17 +2408,17 @@ var ContentPolicyInternalPathSchema = import_zod4.z.string().min(1).max(2048).re
2283
2408
  }, "\uCF58\uD150\uCE20 \uACBD\uB85C\uB294 \uC548\uC804\uD55C \uC0AC\uC774\uD2B8 \uB0B4\uBD80 \uACBD\uB85C\uC5EC\uC57C \uD569\uB2C8\uB2E4");
2284
2409
  var field = { field: ContentPolicyFieldNameSchema };
2285
2410
  var scalarChecks = [
2286
- import_zod4.z.object({ ...field, op: import_zod4.z.literal("equals"), value: ContentPolicyScalarSchema }).strict(),
2287
- import_zod4.z.object({ ...field, op: import_zod4.z.literal("notEquals"), value: ContentPolicyScalarSchema }).strict(),
2288
- import_zod4.z.object({ ...field, op: import_zod4.z.literal("oneOf"), values: import_zod4.z.array(ContentPolicyScalarSchema).min(1).max(16), allowEmpty: import_zod4.z.boolean().optional() }).strict(),
2289
- import_zod4.z.object({ ...field, op: import_zod4.z.literal("text") }).strict(),
2290
- import_zod4.z.object({ ...field, op: import_zod4.z.literal("date") }).strict(),
2291
- import_zod4.z.object({ ...field, op: import_zod4.z.literal("safeHttps") }).strict(),
2292
- import_zod4.z.object({ ...field, op: import_zod4.z.literal("allowedPath"), paths: import_zod4.z.array(ContentPolicyInternalPathSchema).min(1).max(512) }).strict()
2411
+ import_zod5.z.object({ ...field, op: import_zod5.z.literal("equals"), value: ContentPolicyScalarSchema }).strict(),
2412
+ import_zod5.z.object({ ...field, op: import_zod5.z.literal("notEquals"), value: ContentPolicyScalarSchema }).strict(),
2413
+ import_zod5.z.object({ ...field, op: import_zod5.z.literal("oneOf"), values: import_zod5.z.array(ContentPolicyScalarSchema).min(1).max(16), allowEmpty: import_zod5.z.boolean().optional() }).strict(),
2414
+ import_zod5.z.object({ ...field, op: import_zod5.z.literal("text") }).strict(),
2415
+ import_zod5.z.object({ ...field, op: import_zod5.z.literal("date") }).strict(),
2416
+ import_zod5.z.object({ ...field, op: import_zod5.z.literal("safeHttps") }).strict(),
2417
+ import_zod5.z.object({ ...field, op: import_zod5.z.literal("allowedPath"), paths: import_zod5.z.array(ContentPolicyInternalPathSchema).min(1).max(512) }).strict()
2293
2418
  ];
2294
- var ContentPolicyScalarCheckSchema = import_zod4.z.discriminatedUnion("op", scalarChecks);
2419
+ var ContentPolicyScalarCheckSchema = import_zod5.z.discriminatedUnion("op", scalarChecks);
2295
2420
  var when = { when: ContentPolicyWhenSchema.optional() };
2296
- var ContentPolicyCheckSchema = import_zod4.z.discriminatedUnion("op", [
2421
+ var ContentPolicyCheckSchema = import_zod5.z.discriminatedUnion("op", [
2297
2422
  scalarChecks[0].extend(when),
2298
2423
  scalarChecks[1].extend(when),
2299
2424
  scalarChecks[2].extend(when),
@@ -2301,26 +2426,26 @@ var ContentPolicyCheckSchema = import_zod4.z.discriminatedUnion("op", [
2301
2426
  scalarChecks[4].extend(when),
2302
2427
  scalarChecks[5].extend(when),
2303
2428
  scalarChecks[6].extend(when),
2304
- import_zod4.z.object({ ...field, ...when, op: import_zod4.z.literal("some"), checks: import_zod4.z.array(ContentPolicyScalarCheckSchema).min(1).max(8) }).strict(),
2305
- import_zod4.z.object({
2429
+ import_zod5.z.object({ ...field, ...when, op: import_zod5.z.literal("some"), checks: import_zod5.z.array(ContentPolicyScalarCheckSchema).min(1).max(8) }).strict(),
2430
+ import_zod5.z.object({
2306
2431
  ...when,
2307
- op: import_zod4.z.literal("bodyContent"),
2308
- allowExcerpt: import_zod4.z.boolean().optional(),
2309
- allowFeaturedImage: import_zod4.z.boolean().optional(),
2310
- additionalUrl: import_zod4.z.object({ ...field, ...when }).strict().optional()
2432
+ op: import_zod5.z.literal("bodyContent"),
2433
+ allowExcerpt: import_zod5.z.boolean().optional(),
2434
+ allowFeaturedImage: import_zod5.z.boolean().optional(),
2435
+ additionalUrl: import_zod5.z.object({ ...field, ...when }).strict().optional()
2311
2436
  }).strict(),
2312
- import_zod4.z.object({ ...when, op: import_zod4.z.literal("bodyText") }).strict()
2437
+ import_zod5.z.object({ ...when, op: import_zod5.z.literal("bodyText") }).strict()
2313
2438
  ]);
2314
- var contentPolicy = import_zod4.z.object({
2315
- visibility: import_zod4.z.array(ContentPolicyCheckSchema).max(24),
2316
- indexing: import_zod4.z.array(ContentPolicyCheckSchema).max(24),
2317
- pathSegmentFormat: import_zod4.z.literal("lower-kebab").optional()
2439
+ var contentPolicy = import_zod5.z.object({
2440
+ visibility: import_zod5.z.array(ContentPolicyCheckSchema).max(24),
2441
+ indexing: import_zod5.z.array(ContentPolicyCheckSchema).max(24),
2442
+ pathSegmentFormat: import_zod5.z.literal("lower-kebab").optional()
2318
2443
  }).strict();
2319
2444
  var ContentPolicySchema = contentPolicy.superRefine((policy, ctx) => {
2320
2445
  const refs = collectContentPolicyFieldRefs(policy);
2321
- if (refs.length > 64) ctx.addIssue({ code: import_zod4.z.ZodIssueCode.custom, message: "\uCF58\uD150\uCE20 \uC815\uCC45\uC740 \uACF5\uAC1C \uD544\uB4DC 64\uAC1C\uAE4C\uC9C0 \uCC38\uC870\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4" });
2446
+ if (refs.length > 64) ctx.addIssue({ code: import_zod5.z.ZodIssueCode.custom, message: "\uCF58\uD150\uCE20 \uC815\uCC45\uC740 \uACF5\uAC1C \uD544\uB4DC 64\uAC1C\uAE4C\uC9C0 \uCC38\uC870\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4" });
2322
2447
  for (const ref of refs) {
2323
- if ((ref.children?.length ?? 0) > 8) ctx.addIssue({ code: import_zod4.z.ZodIssueCode.custom, message: `\uBC18\uBCF5 \uD544\uB4DC ${ref.name}\uC758 \uC790\uC2DD \uCC38\uC870\uB294 8\uAC1C\uAE4C\uC9C0 \uD5C8\uC6A9\uD569\uB2C8\uB2E4` });
2448
+ if ((ref.children?.length ?? 0) > 8) ctx.addIssue({ code: import_zod5.z.ZodIssueCode.custom, message: `\uBC18\uBCF5 \uD544\uB4DC ${ref.name}\uC758 \uC790\uC2DD \uCC38\uC870\uB294 8\uAC1C\uAE4C\uC9C0 \uD5C8\uC6A9\uD569\uB2C8\uB2E4` });
2324
2449
  }
2325
2450
  });
2326
2451
  function collectContentPolicyFieldRefs(policy) {
@@ -2495,9 +2620,9 @@ function evaluateContentPolicy(policy, post, options = {}) {
2495
2620
  }
2496
2621
 
2497
2622
  // src/metadata-video-thumbnail.ts
2498
- var import_zod5 = require("zod");
2499
- var MetadataVideoThumbnailRuleSchema = import_zod5.z.object({
2500
- source: import_zod5.z.literal("youtube"),
2623
+ var import_zod6 = require("zod");
2624
+ var MetadataVideoThumbnailRuleSchema = import_zod6.z.object({
2625
+ source: import_zod6.z.literal("youtube"),
2501
2626
  field: ContentPolicyFieldNameSchema,
2502
2627
  when: ContentPolicyWhenSchema.optional()
2503
2628
  }).strict();
@@ -2530,7 +2655,7 @@ function collectMetadataDefaultFieldRefs(rule) {
2530
2655
  }
2531
2656
 
2532
2657
  // src/metadata-defaults.ts
2533
- var imageOrigin = import_zod6.z.string().max(2048).refine((value) => {
2658
+ var imageOrigin = import_zod7.z.string().max(2048).refine((value) => {
2534
2659
  try {
2535
2660
  const url = new URL(value);
2536
2661
  return url.protocol === "https:" && url.origin === value;
@@ -2538,30 +2663,30 @@ var imageOrigin = import_zod6.z.string().max(2048).refine((value) => {
2538
2663
  return false;
2539
2664
  }
2540
2665
  }, "\uC774\uBBF8\uC9C0 \uD5C8\uC6A9 \uBAA9\uB85D\uC5D0\uB294 \uACBD\uB85C \uC5C6\uB294 HTTPS origin\uB9CC \uC120\uC5B8\uD569\uB2C8\uB2E4");
2541
- var MetadataDefaultRulesSchema = import_zod6.z.object({
2666
+ var MetadataDefaultRulesSchema = import_zod7.z.object({
2542
2667
  title: MetadataTextRuleSchema.optional(),
2543
- titleTemplateMaxLength: import_zod6.z.number().int().min(1).max(1e3).optional(),
2668
+ titleTemplateMaxLength: import_zod7.z.number().int().min(1).max(1e3).optional(),
2544
2669
  description: MetadataTextRuleSchema.optional(),
2545
2670
  /** Code-owned defaults precede site defaults but always follow manual overrides. */
2546
2671
  defaults: MetadataValuesSchema.optional(),
2547
2672
  robots: MetadataRobotsPolicySchema.optional(),
2548
- useFeaturedImage: import_zod6.z.boolean().optional(),
2673
+ useFeaturedImage: import_zod7.z.boolean().optional(),
2549
2674
  videoThumbnail: MetadataVideoThumbnailRuleSchema.optional(),
2550
- useFirstBodyImage: import_zod6.z.boolean().optional(),
2551
- useTermImage: import_zod6.z.boolean().optional(),
2552
- imageAltSource: import_zod6.z.enum(["content", "title"]).optional(),
2553
- legacyImageSource: import_zod6.z.literal("legacy-thumbnail").optional(),
2554
- imageAllowedOrigins: import_zod6.z.array(imageOrigin).min(1).max(32).optional(),
2555
- legacyImageAllowedOrigins: import_zod6.z.array(imageOrigin).min(1).max(32).optional(),
2556
- normalizeCloudflareImages: import_zod6.z.boolean().optional(),
2675
+ useFirstBodyImage: import_zod7.z.boolean().optional(),
2676
+ useTermImage: import_zod7.z.boolean().optional(),
2677
+ imageAltSource: import_zod7.z.enum(["content", "title"]).optional(),
2678
+ legacyImageSource: import_zod7.z.literal("legacy-thumbnail").optional(),
2679
+ imageAllowedOrigins: import_zod7.z.array(imageOrigin).min(1).max(32).optional(),
2680
+ legacyImageAllowedOrigins: import_zod7.z.array(imageOrigin).min(1).max(32).optional(),
2681
+ normalizeCloudflareImages: import_zod7.z.boolean().optional(),
2557
2682
  fallback: MetadataValuesSchema.optional()
2558
2683
  }).strict().refine((rule) => !rule.legacyImageSource || Boolean(rule.legacyImageAllowedOrigins?.length), "\uC774\uC804 \uC774\uBBF8\uC9C0 \uC6D0\uBCF8\uC5D0\uB294 \uD5C8\uC6A9 origin \uC120\uC5B8\uC774 \uD544\uC694\uD569\uB2C8\uB2E4").superRefine((rule, context) => {
2559
2684
  if (!rule.videoThumbnail) return;
2560
2685
  const refs = collectMetadataDefaultFieldRefs(rule);
2561
- if (refs.length > 64) context.addIssue({ code: import_zod6.z.ZodIssueCode.custom, message: "\uC601\uC0C1 \uC790\uB3D9\uAC12\uC740 \uACF5\uAC1C \uD544\uB4DC 64\uAC1C\uAE4C\uC9C0 \uCC38\uC870\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4" });
2686
+ if (refs.length > 64) context.addIssue({ code: import_zod7.z.ZodIssueCode.custom, message: "\uC601\uC0C1 \uC790\uB3D9\uAC12\uC740 \uACF5\uAC1C \uD544\uB4DC 64\uAC1C\uAE4C\uC9C0 \uCC38\uC870\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4" });
2562
2687
  for (const { name } of refs) {
2563
2688
  if (!ContentPolicyFieldNameSchema.safeParse(name).success) context.addIssue({
2564
- code: import_zod6.z.ZodIssueCode.custom,
2689
+ code: import_zod7.z.ZodIssueCode.custom,
2565
2690
  message: "\uC601\uC0C1 \uC790\uB3D9\uAC12\uC758 \uD14D\uC2A4\uD2B8\xB7\uC870\uAC74\uC740 \uC548\uC804\uD55C \uACF5\uAC1C \uD544\uB4DC \uC774\uB984\uC744 \uC0AC\uC6A9\uD569\uB2C8\uB2E4"
2566
2691
  });
2567
2692
  }
@@ -2625,20 +2750,20 @@ function formatMetadataDefaults(input, declaration) {
2625
2750
  }
2626
2751
 
2627
2752
  // src/content-query.ts
2628
- var import_zod7 = require("zod");
2753
+ var import_zod8 = require("zod");
2629
2754
  var field2 = { field: ContentPolicyFieldNameSchema };
2630
- var ContentQueryFilterSchema = import_zod7.z.discriminatedUnion("op", [
2631
- import_zod7.z.object({ ...field2, op: import_zod7.z.literal("equals"), value: ContentPolicyScalarSchema }).strict(),
2632
- import_zod7.z.object({ ...field2, op: import_zod7.z.literal("notEquals"), value: ContentPolicyScalarSchema }).strict(),
2633
- import_zod7.z.object({ ...field2, op: import_zod7.z.literal("oneOf"), values: import_zod7.z.array(ContentPolicyScalarSchema).min(1).max(16), allowEmpty: import_zod7.z.boolean().optional() }).strict()
2755
+ var ContentQueryFilterSchema = import_zod8.z.discriminatedUnion("op", [
2756
+ import_zod8.z.object({ ...field2, op: import_zod8.z.literal("equals"), value: ContentPolicyScalarSchema }).strict(),
2757
+ import_zod8.z.object({ ...field2, op: import_zod8.z.literal("notEquals"), value: ContentPolicyScalarSchema }).strict(),
2758
+ import_zod8.z.object({ ...field2, op: import_zod8.z.literal("oneOf"), values: import_zod8.z.array(ContentPolicyScalarSchema).min(1).max(16), allowEmpty: import_zod8.z.boolean().optional() }).strict()
2634
2759
  ]);
2635
- var ContentQuerySchema = import_zod7.z.object({
2636
- modelKeys: import_zod7.z.array(import_zod7.z.string().min(1).max(100).regex(/^[a-z][a-z0-9_-]*$/)).min(1).max(16),
2637
- filters: import_zod7.z.array(ContentQueryFilterSchema).max(8).optional(),
2638
- scope: import_zod7.z.enum(["currentTerm", "currentPath"]).optional(),
2760
+ var ContentQuerySchema = import_zod8.z.object({
2761
+ modelKeys: import_zod8.z.array(import_zod8.z.string().min(1).max(100).regex(/^[a-z][a-z0-9_-]*$/)).min(1).max(16),
2762
+ filters: import_zod8.z.array(ContentQueryFilterSchema).max(8).optional(),
2763
+ scope: import_zod8.z.enum(["currentTerm", "currentPath"]).optional(),
2639
2764
  pinnedField: ContentPolicyFieldNameSchema.optional(),
2640
- pageSize: import_zod7.z.number().int().min(1).max(100),
2641
- requireItems: import_zod7.z.boolean().optional()
2765
+ pageSize: import_zod8.z.number().int().min(1).max(100),
2766
+ requireItems: import_zod8.z.boolean().optional()
2642
2767
  }).strict();
2643
2768
  function collectContentQueryFieldRefs(query) {
2644
2769
  return [.../* @__PURE__ */ new Set([...(query.filters ?? []).map((filter) => filter.field), ...query.pinnedField ? [query.pinnedField] : []])].map((name) => ({ name }));
@@ -2703,6 +2828,190 @@ function selectContentQuery(query, posts, policies, options) {
2703
2828
  return evaluateContentQuery(query, candidates, options);
2704
2829
  }
2705
2830
 
2831
+ // src/content-relations.ts
2832
+ function assertScope(scope) {
2833
+ if (!scope.tenantId.trim() || !scope.siteId.trim()) throw new Error("Content scope requires tenantId and siteId");
2834
+ }
2835
+ function publishedCandidates(candidates, scope, sourceId) {
2836
+ const seen = /* @__PURE__ */ new Set();
2837
+ return candidates.filter((candidate) => {
2838
+ if (candidate.tenantId !== scope.tenantId || candidate.siteId !== scope.siteId || candidate.id === sourceId || candidate.status !== "published" || !candidate.id.trim() || !candidate.title.trim() || !ContentPolicyInternalPathSchema.safeParse(candidate.path).success || !Number.isFinite(Date.parse(candidate.publishedAt ?? "")) || seen.has(candidate.id)) return false;
2839
+ seen.add(candidate.id);
2840
+ return true;
2841
+ });
2842
+ }
2843
+ function latestFirst(left, right) {
2844
+ return Date.parse(right.publishedAt) - Date.parse(left.publishedAt) || (left.id < right.id ? -1 : left.id > right.id ? 1 : 0);
2845
+ }
2846
+ function selectRelatedContent(candidates, options) {
2847
+ assertScope(options.scope);
2848
+ const termIds = new Set(options.termIds.filter((id) => id.trim()));
2849
+ if (options.includeDescendants) {
2850
+ for (const termId of options.termIds) {
2851
+ if (!termId.trim() || !Object.hasOwn(options.descendantTermIds ?? {}, termId)) continue;
2852
+ for (const descendantId of options.descendantTermIds[termId] ?? []) {
2853
+ if (descendantId.trim()) termIds.add(descendantId);
2854
+ }
2855
+ }
2856
+ }
2857
+ const selected = publishedCandidates(candidates, options.scope, options.sourceId).filter((candidate) => candidate.termIds.some((id) => termIds.has(id))).sort(latestFirst);
2858
+ return options.models.map(({ modelKey, maxCount }) => {
2859
+ if (!Number.isSafeInteger(maxCount) || maxCount < 0) throw new Error("Related content maxCount must be a non-negative integer");
2860
+ return { modelKey, items: selected.filter((candidate) => candidate.modelKey === modelKey).slice(0, maxCount) };
2861
+ });
2862
+ }
2863
+ function resolveContentCta(candidates, options) {
2864
+ assertScope(options.scope);
2865
+ const { mode, targetPostId } = options.cta;
2866
+ const hidden = (reason) => ({ state: "hidden", mode, reason });
2867
+ if (mode === "off") return hidden("disabled");
2868
+ const published = publishedCandidates(candidates, options.scope, options.sourceId);
2869
+ if (mode === "manual") {
2870
+ if (!targetPostId?.trim()) return hidden("manual_target_missing");
2871
+ const target = published.find((candidate) => candidate.id === targetPostId);
2872
+ return target ? { state: "resolved", mode, target } : hidden("manual_target_unavailable");
2873
+ }
2874
+ if (!options.primaryTermId?.trim()) return hidden("primary_term_missing");
2875
+ const targets = published.filter((candidate) => options.detailModelKeys.includes(candidate.modelKey) && candidate.termIds.includes(options.primaryTermId));
2876
+ if (targets.length === 0) return hidden("auto_target_missing");
2877
+ if (targets.length > 1) return hidden("auto_target_ambiguous");
2878
+ return { state: "resolved", mode, target: targets[0] };
2879
+ }
2880
+ function plainFact(value) {
2881
+ return value.replace(/\s+/gu, " ").trim();
2882
+ }
2883
+ function selectVisibleFaqEntries(entries) {
2884
+ const seen = /* @__PURE__ */ new Set();
2885
+ const selected = [];
2886
+ for (const entry of entries) {
2887
+ const id = entry.id.trim();
2888
+ const question = plainFact(entry.question);
2889
+ const answer = plainFact(entry.answer);
2890
+ if (!entry.visible || entry.deleted || !id || !question || !answer || seen.has(id)) continue;
2891
+ seen.add(id);
2892
+ selected.push({ id, question, answer });
2893
+ }
2894
+ return selected;
2895
+ }
2896
+ function visibleFaqPageJsonLd(entries) {
2897
+ return faqPageJsonLd(selectVisibleFaqEntries(entries));
2898
+ }
2899
+ function enteredNames(value) {
2900
+ const values = typeof value === "string" ? [value] : value ?? [];
2901
+ return [...new Set(values.map(plainFact).filter(Boolean))];
2902
+ }
2903
+ function profileQualificationJsonLdProperties(input) {
2904
+ const qualifications = enteredNames(input.qualification);
2905
+ const alumni = enteredNames(input.alumniOf);
2906
+ return {
2907
+ ...qualifications.length ? { hasCredential: qualifications.map((name) => ({ "@type": "EducationalOccupationalCredential", name })) } : {},
2908
+ ...alumni.length ? { alumniOf: alumni.map((name) => ({ "@type": "EducationalOrganization", name })) } : {}
2909
+ };
2910
+ }
2911
+
2912
+ // src/content-values.ts
2913
+ var import_zod9 = require("zod");
2914
+ var itemIdSchema = import_zod9.z.string().uuid();
2915
+ var forbiddenKeys = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
2916
+ function isContentRecord(value) {
2917
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
2918
+ const prototype = Object.getPrototypeOf(value);
2919
+ return prototype === Object.prototype || prototype === null;
2920
+ }
2921
+ function validatePath(path) {
2922
+ if (!Array.isArray(path) || !path.length || path.some((key) => typeof key !== "string" || !key || forbiddenKeys.has(key) || /^\d+$/u.test(key) || /[\u0000-\u001f]/u.test(key))) {
2923
+ throw new Error("Content path must use safe property names and stable item IDs");
2924
+ }
2925
+ }
2926
+ function findItemIndex(items, token) {
2927
+ if (!token.startsWith("@") || !itemIdSchema.safeParse(token.slice(1)).success) {
2928
+ throw new Error("Content repeater path requires @<uuid>");
2929
+ }
2930
+ const targetId = token.slice(1);
2931
+ const seen = /* @__PURE__ */ new Set();
2932
+ let selected = -1;
2933
+ for (const [index, item] of items.entries()) {
2934
+ if (!isContentRecord(item) || !Object.hasOwn(item, "_id")) throw new Error("Content repeater item has no stable ID");
2935
+ const parsed = itemIdSchema.safeParse(item._id);
2936
+ if (!parsed.success) throw new Error("Content repeater item has an invalid ID");
2937
+ if (seen.has(parsed.data)) throw new Error("Content repeater item IDs are ambiguous");
2938
+ seen.add(parsed.data);
2939
+ if (parsed.data === targetId) selected = index;
2940
+ }
2941
+ if (selected < 0) throw new Error("Content repeater item was not found");
2942
+ return selected;
2943
+ }
2944
+ function locateContentValue(values, path) {
2945
+ validatePath(path);
2946
+ if (!isContentRecord(values)) throw new Error("Content values must be a serialized object");
2947
+ const frames = [];
2948
+ let current = values;
2949
+ for (const key of path) {
2950
+ if (Array.isArray(current)) {
2951
+ const index = findItemIndex(current, key);
2952
+ frames.push({ kind: "array", parent: current, index, itemId: key.slice(1) });
2953
+ current = current[index];
2954
+ } else {
2955
+ if (!isContentRecord(current) || key.startsWith("@") || !Object.hasOwn(current, key)) {
2956
+ throw new Error("Content property was not found");
2957
+ }
2958
+ frames.push({ kind: "object", parent: current, key });
2959
+ current = current[key];
2960
+ }
2961
+ }
2962
+ return { value: current, frames };
2963
+ }
2964
+ function getContentValue(values, path) {
2965
+ return locateContentValue(values, path).value;
2966
+ }
2967
+ function setContentValue(values, path, value) {
2968
+ const { frames } = locateContentValue(values, path);
2969
+ const leaf = frames[frames.length - 1];
2970
+ if (leaf.kind === "object" && leaf.key === "_id" || leaf.kind === "array" && (!isContentRecord(value) || !Object.hasOwn(value, "_id") || value._id !== leaf.itemId)) {
2971
+ throw new Error("Content edits must preserve stable item IDs");
2972
+ }
2973
+ let patched = value;
2974
+ for (let index = frames.length - 1; index >= 0; index--) {
2975
+ const frame = frames[index];
2976
+ if (frame.kind === "array") {
2977
+ const items = [...frame.parent];
2978
+ items[frame.index] = patched;
2979
+ patched = items;
2980
+ } else {
2981
+ patched = { ...frame.parent, [frame.key]: patched };
2982
+ }
2983
+ }
2984
+ return patched;
2985
+ }
2986
+
2987
+ // src/content-dependencies.ts
2988
+ function inScope(value, scope) {
2989
+ return value.tenantId === scope.tenantId && value.siteId === scope.siteId;
2990
+ }
2991
+ function matchesQuery(entity, query) {
2992
+ return query.modelKeys.includes(entity.modelKey) && (query.termIds === void 0 || entity.termIds.some((id) => id.trim() && query.termIds.includes(id)));
2993
+ }
2994
+ function resolveContentDependencyPaths(dependencies, change) {
2995
+ const { scope } = change;
2996
+ if (!scope.tenantId.trim() || !scope.siteId.trim()) throw new Error("Content scope requires tenantId and siteId");
2997
+ const snapshots = [change.before, change.after].filter((entity) => entity != null && inScope(entity, scope));
2998
+ const entityIds = new Set([...snapshots.map(({ id }) => id), ...change.entityIds ?? []].filter((id) => id.trim()));
2999
+ const singletonKeys = new Set((change.singletonKeys ?? []).filter((key) => key.trim()));
3000
+ const paths = /* @__PURE__ */ new Set();
3001
+ const includePath = (path) => {
3002
+ if (path !== null && ContentPolicyInternalPathSchema.safeParse(path).success) paths.add(path);
3003
+ };
3004
+ for (const snapshot of snapshots) includePath(snapshot.path);
3005
+ for (const dependency of dependencies) {
3006
+ if (!inScope(dependency, scope)) continue;
3007
+ const direct = dependency.entityIds?.some((id) => entityIds.has(id));
3008
+ const shared = dependency.singletonKeys?.some((key) => singletonKeys.has(key));
3009
+ const queried = dependency.queries?.some((query) => snapshots.some((entity) => matchesQuery(entity, query)));
3010
+ if (direct || shared || queried) includePath(dependency.path);
3011
+ }
3012
+ return [...paths].sort();
3013
+ }
3014
+
2706
3015
  // src/redirect-paths.ts
2707
3016
  var CONTROL_OR_BACKSLASH = /[\\\u0000-\u001f\u007f]/u;
2708
3017
  var PROTECTED_PREFIX = /^\/(?:api|_next|_vercel|auth|admin|login|logout|revalidate|preview|assets|static|images|fonts|\.well-known)(?:\/|$)/i;
@@ -2804,11 +3113,15 @@ function normalizeRedirectTarget(raw) {
2804
3113
  SITE_PATTERN_KEY_PATTERN,
2805
3114
  SITE_PATTERN_PLACEMENTS_KEY,
2806
3115
  SITE_PATTERN_SLOTS,
3116
+ VISUAL_EDIT_ATTRIBUTE,
3117
+ VISUAL_EDIT_PROTOCOL,
3118
+ VISUAL_EDIT_PROTOCOL_VERSION,
2807
3119
  applyBlockBindings,
2808
3120
  bindingSrcValue,
2809
3121
  bindingTextValue,
2810
3122
  blockSchema,
2811
3123
  boundFieldValue,
3124
+ buildVisualEditPreviewUrl,
2812
3125
  chainResolvers,
2813
3126
  cmsFontSize,
2814
3127
  cmsImageAlign,
@@ -2834,6 +3147,7 @@ function normalizeRedirectTarget(raw) {
2834
3147
  findSitePatternPlacementRule,
2835
3148
  formatMetadataDefaults,
2836
3149
  formatSectionLockViolations,
3150
+ getContentValue,
2837
3151
  htmlToPlainText,
2838
3152
  injectStableIdsTree,
2839
3153
  internalContentKeyFromPath,
@@ -2848,6 +3162,7 @@ function normalizeRedirectTarget(raw) {
2848
3162
  isSitePatternSlotKey,
2849
3163
  isValidBlock,
2850
3164
  isValidSitePatternKey,
3165
+ isVisualEditOrigin,
2851
3166
  issuePreviewToken,
2852
3167
  matchesContentScalarCheck,
2853
3168
  mergeMetadataPatch,
@@ -2861,9 +3176,12 @@ function normalizeRedirectTarget(raw) {
2861
3176
  normalizeRedirectSource,
2862
3177
  normalizeRedirectTarget,
2863
3178
  parseBlock,
3179
+ parseVisualEditAddress,
3180
+ parseVisualEditMessage,
2864
3181
  postsForCollection,
2865
3182
  postsInAnyCollection,
2866
3183
  postsNotInAnyCollection,
3184
+ profileQualificationJsonLdProperties,
2867
3185
  readMetadataValues,
2868
3186
  readSitePatternPlacements,
2869
3187
  readSitePatternPresentation,
@@ -2876,6 +3194,8 @@ function normalizeRedirectTarget(raw) {
2876
3194
  resolveCategoryTreeEntryPath,
2877
3195
  resolveCategoryTreeHubPaths,
2878
3196
  resolveCategoryTreeSelection,
3197
+ resolveContentCta,
3198
+ resolveContentDependencyPaths,
2879
3199
  resolveMetadata,
2880
3200
  resolvePatternSlots,
2881
3201
  resolvePostBodySource,
@@ -2884,16 +3204,27 @@ function normalizeRedirectTarget(raw) {
2884
3204
  resolvePostPath,
2885
3205
  resolvePostPublicPath,
2886
3206
  resolvePostUrl,
3207
+ sameVisualDependencies,
3208
+ sameVisualPreviewTarget,
2887
3209
  sanitizeHtml,
2888
3210
  sanitizeImportedCss,
2889
3211
  sanitizeImportedHtml,
2890
3212
  scopeImportedCss,
2891
3213
  selectContentQuery,
3214
+ selectRelatedContent,
3215
+ selectVisibleFaqEntries,
2892
3216
  serializeBlock,
3217
+ setContentValue,
2893
3218
  setSitePatternPlacementRule,
2894
3219
  sitePatternButtonColor,
2895
3220
  sitePatternPresentationAttributes,
2896
3221
  validateCollections,
2897
- verifyPreviewToken
3222
+ verifyPreviewToken,
3223
+ visibleFaqPageJsonLd,
3224
+ visualEditAddressKey,
3225
+ visualEditAddressSchema,
3226
+ visualEditAttributes,
3227
+ visualPreviewTargetSchema,
3228
+ visualRenderReceiptSchema
2898
3229
  });
2899
3230
  //# sourceMappingURL=index.cjs.map