@riverbankcms/sdk 0.60.7 → 0.60.9

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.
Files changed (33) hide show
  1. package/dist/_dts/api/src/navigation/linkValue.d.ts +1 -1
  2. package/dist/_dts/blocks/src/index.d.ts +2 -1
  3. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +185 -0
  4. package/dist/_dts/blocks/src/system/manifest/index.d.ts +1 -0
  5. package/dist/_dts/blocks/src/system/manifest/validation.d.ts +2 -84
  6. package/dist/_dts/blocks/src/system/types/link.d.ts +93 -1
  7. package/dist/_dts/db/src/schemas/forms.d.ts +24 -3
  8. package/dist/_dts/editor-blocks/src/widgets/link/LinkSummary.d.ts +1 -1
  9. package/dist/_dts/editor-blocks/src/widgets/link/LinkWidget.d.ts +1 -1
  10. package/dist/_dts/editor-blocks/src/widgets/link/utils.d.ts +1 -1
  11. package/dist/_dts/sdk/src/config/typed-entries.d.ts +36 -1
  12. package/dist/_dts/sdk/src/contracts/content.d.ts +6 -1
  13. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  14. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +1 -0
  15. package/dist/_dts/sdk/src/version.d.ts +1 -1
  16. package/dist/cli/index.mjs +676 -428
  17. package/dist/client/client.mjs +39991 -39754
  18. package/dist/client/hooks.mjs +1737 -1735
  19. package/dist/client/rendering.mjs +1463 -1226
  20. package/dist/preview-next/client/runtime.mjs +1052 -799
  21. package/dist/server/components.mjs +2983 -2745
  22. package/dist/server/config-validation.mjs +1727 -1725
  23. package/dist/server/config.mjs +1727 -1725
  24. package/dist/server/data.mjs +1737 -1735
  25. package/dist/server/index.mjs +1 -1
  26. package/dist/server/next.mjs +3026 -2788
  27. package/dist/server/page-converter.mjs +10 -10
  28. package/dist/server/prebuild.mjs +1 -1
  29. package/dist/server/rendering/server.mjs +2996 -2758
  30. package/dist/server/rendering.mjs +2996 -2758
  31. package/dist/server/routing.mjs +256 -285
  32. package/dist/server/server.mjs +1738 -1736
  33. package/package.json +1 -1
@@ -1522,15 +1522,6 @@ var init_augmentManifest = __esm({
1522
1522
  "../blocks/src/system/manifest/augmentManifest.ts"() {
1523
1523
  }
1524
1524
  });
1525
-
1526
- // ../blocks/src/lib/typeGuards.ts
1527
- function isRecord2(value) {
1528
- return typeof value === "object" && value !== null && !Array.isArray(value);
1529
- }
1530
- var init_typeGuards = __esm({
1531
- "../blocks/src/lib/typeGuards.ts"() {
1532
- }
1533
- });
1534
1525
  var VARIANT_ROLES, cornerStyleSchema, shadowSizeSchema, textTransformSchema, fontWeightSchema, buttonTypographySchema, letterSpacingSchema, hoverTransformSchema, hoverColorSchema, buttonPaddingPresetSchema, gradientStyleSchema, gradientSharpnessSchema, prioritySchema, buttonSizeNameSchema, PADDING_TOKEN_PATTERN, paddingShorthandSchema, buttonSizeConfigSchema, buttonSizesSchema, buttonGlobalSettingsSchema, gradientDirectionSchema, buttonBackgroundSchema, effectApplicationSchema, buttonBorderSchema, variantShadowSchema, variantEffectsSchema, variantSizeOverridesSchema, buttonVariantSchema, buttonSystemSchema;
1535
1526
  var init_types2 = __esm({
1536
1527
  "../theme-core/src/buttons/types.ts"() {
@@ -8330,9 +8321,9 @@ function createZodCodec(schema) {
8330
8321
  return decodeSuccess(parsed.data);
8331
8322
  }
8332
8323
  return decodeFailure(
8333
- parsed.error.issues.map((issue) => ({
8334
- path: zodIssuePath(issue.path),
8335
- message: issue.message
8324
+ parsed.error.issues.map((issue2) => ({
8325
+ path: zodIssuePath(issue2.path),
8326
+ message: issue2.message
8336
8327
  }))
8337
8328
  );
8338
8329
  }
@@ -8622,6 +8613,15 @@ var init_colorConversion = __esm({
8622
8613
  }
8623
8614
  });
8624
8615
 
8616
+ // ../blocks/src/lib/typeGuards.ts
8617
+ function isRecord2(value) {
8618
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8619
+ }
8620
+ var init_typeGuards = __esm({
8621
+ "../blocks/src/lib/typeGuards.ts"() {
8622
+ }
8623
+ });
8624
+
8625
8625
  // ../theme-core/src/responsiveDisplay.ts
8626
8626
  function mobileOnlyDisplay(display) {
8627
8627
  return `${BASE_DISPLAY_CLASSES[display]} rb-lg-hidden`;
@@ -11191,7 +11191,7 @@ var init_shared6 = __esm({
11191
11191
  });
11192
11192
 
11193
11193
  // ../blocks/src/system/runtime/api/creditProducts.ts
11194
- function assertNever3(value) {
11194
+ function assertNever4(value) {
11195
11195
  throw new Error(`Unhandled credit product case: ${String(value)}`);
11196
11196
  }
11197
11197
  function useCasesFromModules(modules) {
@@ -11224,7 +11224,7 @@ function getCreditProductCreditUnit(useCase) {
11224
11224
  perCreditSuffix: "credit"
11225
11225
  };
11226
11226
  default:
11227
- return assertNever3(useCase);
11227
+ return assertNever4(useCase);
11228
11228
  }
11229
11229
  }
11230
11230
  function deriveCreditProductLabels(products, options = {}) {
@@ -20792,11 +20792,11 @@ siteBannerSettingsSchema.superRefine((value, ctx) => {
20792
20792
  return;
20793
20793
  }
20794
20794
  const analysis = analyzeSiteBannerBody(value.body);
20795
- for (const issue of analysis.issues) {
20795
+ for (const issue2 of analysis.issues) {
20796
20796
  ctx.addIssue({
20797
20797
  code: z.ZodIssueCode.custom,
20798
20798
  path: ["body"],
20799
- message: siteBannerBodyIssueMessage(issue)
20799
+ message: siteBannerBodyIssueMessage(issue2)
20800
20800
  });
20801
20801
  }
20802
20802
  });
@@ -20830,8 +20830,8 @@ function analyzeSiteBannerBody(body) {
20830
20830
  issues
20831
20831
  };
20832
20832
  }
20833
- function siteBannerBodyIssueMessage(issue) {
20834
- switch (issue) {
20833
+ function siteBannerBodyIssueMessage(issue2) {
20834
+ switch (issue2) {
20835
20835
  case "empty":
20836
20836
  return "Banner message is required when the banner is enabled.";
20837
20837
  case "too-many-paragraphs":
@@ -21083,324 +21083,35 @@ function isNavigationMenuValidationItem(value) {
21083
21083
  if (!isRecord(value)) return false;
21084
21084
  return typeof value.label === "string";
21085
21085
  }
21086
- var Rfc6902PatchOp = z.discriminatedUnion("op", [
21087
- // Standard RFC-6902 operations
21088
- z.object({
21089
- op: z.literal("add"),
21090
- path: z.string(),
21091
- value: z.unknown()
21092
- }),
21093
- z.object({
21094
- op: z.literal("remove"),
21095
- path: z.string()
21096
- }),
21097
- z.object({
21098
- op: z.literal("replace"),
21099
- path: z.string(),
21100
- value: z.unknown()
21101
- }),
21102
- z.object({
21103
- op: z.literal("move"),
21104
- from: z.string(),
21105
- path: z.string()
21106
- }),
21107
- z.object({
21108
- op: z.literal("copy"),
21109
- from: z.string(),
21110
- path: z.string()
21111
- }),
21112
- // Block-level operations (Phase 2)
21113
- z.object({
21114
- op: z.literal("add_block"),
21115
- blockKind: z.string(),
21116
- afterBlockId: z.string().nullable(),
21117
- content: z.record(z.string(), z.unknown()),
21118
- rationale: z.string()
21119
- }),
21120
- z.object({
21121
- op: z.literal("delete_block"),
21122
- blockId: z.string(),
21123
- rationale: z.string()
21124
- }),
21125
- z.object({
21126
- op: z.literal("reorder_block"),
21127
- blockId: z.string(),
21128
- afterBlockId: z.string().nullable(),
21129
- rationale: z.string()
21130
- })
21131
- ]);
21132
- z.object({
21133
- request: z.string().min(1).max(2e3)
21134
- });
21135
- var MultiPagePatchEnvelope = z.object({
21136
- pageId: z.string(),
21137
- blockId: z.string().optional(),
21138
- // Not present for page-level ops
21139
- blockKind: z.string().optional(),
21140
- blockPurpose: z.string().optional().nullable(),
21141
- ops: z.array(Rfc6902PatchOp),
21142
- rationale: z.string(),
21143
- currentContent: z.record(z.string(), z.unknown()).optional()
21144
- });
21145
- z.object({
21146
- patches: z.array(MultiPagePatchEnvelope),
21147
- assistantMessage: z.string(),
21148
- pagesModified: z.number(),
21149
- toolCallsUsed: z.number()
21150
- });
21151
-
21152
- // ../blocks/src/system/manifest/registry.ts
21153
- var REGISTRY_SYMBOL = /* @__PURE__ */ Symbol.for("@riverbankcms/blocks/manifest-registry");
21154
- var isVitest = typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.VITEST === "true";
21155
- var globalScope = globalThis;
21156
- var manifestStore = isVitest ? /* @__PURE__ */ new Map() : globalScope[REGISTRY_SYMBOL] ??= /* @__PURE__ */ new Map();
21157
- function registerManifest(manifest) {
21158
- manifestStore.set(manifest.name, manifest);
21159
- return manifest;
21160
- }
21161
-
21162
- // ../blocks/src/system/manifest/validation.ts
21163
- init_schema2();
21164
-
21165
- // ../blocks/src/system/manifest/richTextSchema.ts
21166
- init_typeGuards();
21167
- var TipTapMarkSchema = z.object({
21168
- type: z.string(),
21169
- attrs: z.record(z.string(), z.unknown()).optional()
21170
- });
21171
- var TipTapNodeSchema = z.lazy(
21172
- () => z.object({
21173
- type: z.string(),
21174
- content: z.array(TipTapNodeSchema).optional(),
21175
- text: z.string().optional(),
21176
- attrs: z.record(z.string(), z.unknown()).optional(),
21177
- marks: z.array(TipTapMarkSchema).optional()
21178
- })
21179
- );
21180
- var RichTextValueSchema = z.union([
21181
- // New format: TipTap document directly
21182
- TipTapNodeSchema,
21183
- // Legacy format: wrapped in { doc: {...} }
21184
- z.object({
21185
- doc: TipTapNodeSchema
21186
- })
21187
- ]);
21188
- function buildRichTextSchema(options) {
21189
- let schema = RichTextValueSchema;
21190
- return schema;
21191
- }
21192
-
21193
- // ../blocks/src/system/manifest/validation.ts
21194
- function isEmpty(value) {
21195
- if (typeof value === "string" && value.trim() === "") return true;
21196
- if (value === null || value === void 0) return true;
21197
- if (Array.isArray(value) && value.length === 0) return true;
21198
- if (isEmptyRichTextDoc(value)) return true;
21199
- return false;
21200
- }
21201
- function isEmptyRichTextDoc(value) {
21202
- if (!value || typeof value !== "object") return false;
21203
- const obj = value;
21204
- const doc = "doc" in obj ? obj.doc : obj;
21205
- if (!doc || typeof doc !== "object") return false;
21206
- const docObj = doc;
21207
- if (docObj.type !== "doc") return false;
21208
- const content = docObj.content;
21209
- if (!Array.isArray(content) || content.length === 0) return true;
21210
- if (content.length === 1) {
21211
- const first = content[0];
21212
- if (first.type === "paragraph" && (!first.content || first.content.length === 0)) {
21213
- return true;
21214
- }
21215
- }
21216
- return false;
21217
- }
21218
- function preprocessEmptyValues(content) {
21219
- if (!content || typeof content !== "object" || Array.isArray(content)) {
21220
- return {};
21221
- }
21222
- const result = {};
21223
- for (const [key, value] of Object.entries(content)) {
21224
- result[key] = isEmpty(value) ? void 0 : value;
21225
- }
21226
- return result;
21227
- }
21228
- function createManifestValidator(manifestOrFields, options = {}) {
21229
- const allowNull = options.allowNull ?? true;
21230
- const isDraft = options.mode === "draft";
21231
- const allowIncomplete = isDraft || (options.allowIncomplete ?? false);
21232
- const shape = {};
21233
- const fields3 = Array.isArray(manifestOrFields) ? manifestOrFields : manifestOrFields.fields ?? [];
21234
- for (const field of fields3) {
21235
- shape[field.id] = buildFieldSchema(field, allowNull, allowIncomplete);
21236
- }
21237
- const baseSchema = z.object(shape).catchall(z.unknown()).passthrough();
21238
- if (isDraft) {
21239
- return z.preprocess(preprocessEmptyValues, baseSchema);
21240
- }
21241
- return baseSchema;
21242
- }
21243
- function buildFieldSchema(field, allowNull, allowIncomplete = false) {
21244
- const required = Boolean(field.required);
21245
- let schema;
21246
- switch (field.type) {
21247
- case "text":
21248
- schema = buildTextSchema(field);
21249
- break;
21250
- case "richText":
21251
- schema = buildRichTextSchema2();
21252
- break;
21253
- case "media": {
21254
- const baseMediaSchema = z.record(z.string(), z.unknown());
21255
- const isRequired = Boolean(field.required);
21256
- schema = isRequired ? baseMediaSchema : baseMediaSchema.nullable();
21257
- break;
21258
- }
21259
- case "boolean":
21260
- schema = z.boolean();
21261
- break;
21262
- case "slug":
21263
- schema = buildSlugSchema(field);
21264
- break;
21265
- case "url":
21266
- schema = buildUrlSchema(field);
21267
- break;
21268
- case "link":
21269
- schema = buildLinkSchema();
21270
- break;
21271
- case "select":
21272
- schema = buildSelectSchema(field);
21273
- break;
21274
- case "reference":
21275
- schema = buildReferenceSchema(field);
21276
- break;
21277
- case "repeater":
21278
- schema = buildRepeaterSchema(field, allowNull, allowIncomplete);
21279
- break;
21280
- case "group":
21281
- schema = buildGroupSchema(field, allowNull, allowIncomplete);
21282
- break;
21283
- default:
21284
- schema = z.unknown();
21285
- break;
21286
- }
21287
- return finalizeSchema(schema, required, allowNull, allowIncomplete);
21288
- }
21289
- function buildFriendlyStringSchema(field) {
21290
- return z.string({
21291
- error: (issue) => issue.input === void 0 || issue.input === null ? "This field is required" : `${field.label} needs text`
21292
- });
21293
- }
21294
- function buildTextSchema(field) {
21295
- const ui = field.ui ?? {};
21296
- const inputType = ui?.inputType;
21297
- let schema;
21298
- if (inputType === "number") {
21299
- schema = z.coerce.number({
21300
- error: (issue) => issue.input === void 0 ? `${field.label} is required` : `${field.label} needs a number`
21301
- });
21302
- if (typeof ui?.min === "number") {
21303
- schema = schema.min(
21304
- ui.min,
21305
- `${field.label} must be ${ui.min} or more`
21306
- );
21307
- }
21308
- if (typeof ui?.max === "number") {
21309
- schema = schema.max(
21310
- ui.max,
21311
- `${field.label} must be ${ui.max} or less`
21312
- );
21313
- }
21314
- } else {
21315
- schema = buildFriendlyStringSchema(field);
21316
- if (inputType === "email") {
21317
- schema = schema.email("Enter a valid email address");
21318
- }
21319
- if (inputType === "tel") {
21320
- const TEL_RE = /^[+()0-9\s\-]{3,}$/;
21321
- schema = schema.regex(
21322
- TEL_RE,
21323
- "Enter a valid phone number"
21324
- );
21325
- }
21326
- if (ui?.pattern) {
21327
- try {
21328
- const re = new RegExp(ui.pattern);
21329
- schema = schema.regex(
21330
- re,
21331
- `${field.label} is not in the right format`
21332
- );
21333
- } catch {
21334
- }
21335
- }
21336
- if (field.maxLength) {
21337
- schema = schema.max(
21338
- field.maxLength,
21339
- `${field.label} must be ${field.maxLength} characters or less`
21340
- );
21341
- }
21342
- if (field.required) {
21343
- schema = schema.min(1, { message: `${field.label} is required` }).refine((v) => v.trim().length > 0, { message: `${field.label} is required` });
21344
- }
21345
- }
21346
- return schema;
21347
- }
21348
- function buildRichTextSchema2() {
21349
- return buildRichTextSchema();
21350
- }
21351
- var SLUG_PATTERN = /^(?:[a-z0-9]+(?:-[a-z0-9]+)*)(?:\/(?:[a-z0-9]+(?:-[a-z0-9]+)*))*$/;
21352
- function buildSlugSchema(field) {
21353
- const message = `${field.label} can only use lowercase letters, numbers, and dashes`;
21354
- let schema = buildFriendlyStringSchema(field).regex(SLUG_PATTERN, message);
21355
- if (field.maxLength) {
21356
- schema = schema.max(
21357
- field.maxLength,
21358
- `${field.label} must be ${field.maxLength} characters or less`
21359
- );
21360
- }
21361
- if (field.required) {
21362
- schema = schema.min(1, { message: `${field.label} is required` });
21363
- }
21364
- return schema;
21365
- }
21366
- var ALLOWED_URL_PROTOCOLS = ["http:", "https:"];
21367
- function buildUrlSchema(field) {
21368
- const message = "Enter a valid web address";
21369
- const validate = (value) => {
21370
- if (!value) {
21371
- return !field.required;
21372
- }
21373
- if (field.allowRelative && value.startsWith("/")) {
21374
- return true;
21375
- }
21376
- try {
21377
- const parsed = new URL(value);
21378
- return ALLOWED_URL_PROTOCOLS.includes(
21379
- parsed.protocol
21380
- );
21381
- } catch {
21382
- return false;
21383
- }
21384
- };
21385
- let schema = buildFriendlyStringSchema(field).trim().refine(validate, message);
21386
- if (field.required) {
21387
- schema = schema.min(1, { message: `${field.label} is required` });
21388
- }
21389
- return schema;
21390
- }
21391
21086
  var nullableString = z.union([z.string().min(1), z.null()]);
21392
- var internalLinkSchema = z.object({
21087
+ var resolvedInternalLinkKeys = ["entityId", "entityType", "href", "title", "typeLabel"];
21088
+ var internalRouteOnlyLinkSchema = z.object({
21089
+ kind: z.literal("internal"),
21090
+ routeId: z.string().min(1)
21091
+ }).passthrough().superRefine((value, ctx) => {
21092
+ const hasResolvedMetadata = resolvedInternalLinkKeys.some((key) => value[key] != null);
21093
+ if (!hasResolvedMetadata) return;
21094
+ ctx.addIssue({
21095
+ code: "custom",
21096
+ message: "Internal links with resolved metadata must include entityId, entityType, href, title, and typeLabel"
21097
+ });
21098
+ });
21099
+ var internalResolvedLinkSchema = z.object({
21393
21100
  kind: z.literal("internal"),
21394
21101
  routeId: z.string().min(1),
21395
- entityId: z.string().min(1).optional(),
21396
- entityType: z.enum(["page", "content"]).optional(),
21397
- href: nullableString.optional(),
21398
- title: nullableString.optional(),
21399
- typeLabel: nullableString.optional(),
21102
+ entityId: z.string().min(1),
21103
+ entityType: z.enum(["page", "content"]),
21104
+ href: z.string().min(1),
21105
+ title: z.string().min(1),
21106
+ typeLabel: z.string().min(1),
21400
21107
  contentTypeKey: nullableString.optional(),
21401
21108
  contentTypeName: nullableString.optional(),
21402
21109
  updatedAt: nullableString.optional()
21403
21110
  }).passthrough();
21111
+ var internalLinkSchema = z.union([
21112
+ internalResolvedLinkSchema,
21113
+ internalRouteOnlyLinkSchema
21114
+ ]);
21404
21115
  var externalLinkSchema2 = z.object({
21405
21116
  kind: z.literal("external"),
21406
21117
  href: z.string().min(1)
@@ -21425,84 +21136,556 @@ var linkSchema = z.union([
21425
21136
  pageLinkSchema2,
21426
21137
  entryLinkSchema2
21427
21138
  ]);
21428
- function buildLinkSchema() {
21429
- return linkSchema;
21139
+
21140
+ // ../blocks/src/system/manifest/fieldValidation/index.ts
21141
+ init_schema2();
21142
+ var TEL_RE = /^[+()0-9\s-]{3,}$/;
21143
+ var SLUG_PATTERN = /^(?:[a-z0-9]+(?:-[a-z0-9]+)*)(?:\/(?:[a-z0-9]+(?:-[a-z0-9]+)*))*$/;
21144
+ var ALLOWED_URL_PROTOCOLS = ["http:", "https:"];
21145
+ function assertNever2(value) {
21146
+ throw new Error(`Unhandled field validation variant: ${JSON.stringify(value)}`);
21430
21147
  }
21431
- function assertNeverSelectSource2(value) {
21432
- throw new Error(`Unhandled select source: ${JSON.stringify(value)}`);
21148
+ function validationContext(options = {}) {
21149
+ const isDraft = options.mode === "draft";
21150
+ return {
21151
+ allowNull: options.allowNull ?? true,
21152
+ allowIncomplete: isDraft || (options.allowIncomplete ?? false)
21153
+ };
21433
21154
  }
21434
- function buildSelectSchema(field) {
21435
- const source = normalizeSelectSource(field);
21436
- const singleValueSchema = (() => {
21437
- switch (source.kind) {
21438
- case "sdk":
21439
- case "site":
21440
- return buildFriendlyStringSchema(field);
21441
- case "static": {
21442
- const values = field.options.map((option) => option.value);
21443
- return buildFriendlyStringSchema(field).refine(
21444
- (value) => values.includes(value),
21445
- `Choose one of the available options for ${field.label}`
21446
- );
21447
- }
21448
- default:
21449
- return assertNeverSelectSource2(source);
21450
- }
21451
- })();
21452
- if (field.multiple) {
21453
- const arraySchema = z.array(singleValueSchema);
21454
- return field.required ? arraySchema.min(1, { message: "This field is required" }) : arraySchema.nullable();
21155
+ function fieldIssueToMessage(issue2) {
21156
+ switch (issue2.kind) {
21157
+ case "required":
21158
+ return "This field is required";
21159
+ case "wrongType":
21160
+ return `${issue2.label} needs ${issue2.expected}`;
21161
+ case "tooLong":
21162
+ return `Use ${issue2.maximum} characters or fewer`;
21163
+ case "invalidEmail":
21164
+ return "Enter a valid email address";
21165
+ case "invalidPhone":
21166
+ return "Enter a valid phone number";
21167
+ case "invalidPattern":
21168
+ return "Check the format and try again";
21169
+ case "invalidUrl":
21170
+ return "Enter a valid web address";
21171
+ case "invalidNumber":
21172
+ return `${issue2.label} needs a number`;
21173
+ case "tooSmall":
21174
+ return `${issue2.label} must be ${issue2.minimum} or more`;
21175
+ case "tooLarge":
21176
+ return `${issue2.label} must be ${issue2.maximum} or less`;
21177
+ case "invalidSlug":
21178
+ return `${issue2.label} can only use lowercase letters, numbers, and dashes`;
21179
+ case "invalidOption":
21180
+ return `Choose one of the available options for ${issue2.label}`;
21181
+ case "tooFewItems":
21182
+ return `${issue2.label} must have at least ${issue2.minimum} ${pluralize(issue2.itemLabel, issue2.minimum)}`;
21183
+ case "tooManyItems":
21184
+ return `${issue2.label} must have at most ${issue2.maximum} ${pluralize(issue2.itemLabel, issue2.maximum)}`;
21185
+ case "invalidRichText":
21186
+ return `${issue2.label} must be valid rich text`;
21187
+ case "invalidMedia":
21188
+ return `${issue2.label} must be a valid media item`;
21189
+ case "invalidLink":
21190
+ return `${issue2.label} must be a valid link`;
21191
+ case "invalidReference":
21192
+ return `${issue2.label} must reference a valid item`;
21193
+ default:
21194
+ return assertNever2(issue2);
21455
21195
  }
21456
- return field.required ? singleValueSchema : singleValueSchema.nullable();
21457
21196
  }
21458
- function buildReferenceSchema(field) {
21459
- let schema = z.string({
21460
- error: (issue) => issue.input === void 0 || issue.input === null ? "This field is required" : `${field.label} needs text`
21461
- });
21462
- if (field.required) {
21463
- schema = schema.min(1, { message: `${field.label} is required` });
21197
+ function pluralize(label, count) {
21198
+ if (count === 1) return label;
21199
+ return label.endsWith("s") ? label : `${label}s`;
21200
+ }
21201
+ function deriveFieldValidationPlan(field, path21 = [field.id]) {
21202
+ const required = Boolean(field.required);
21203
+ switch (field.type) {
21204
+ case "text": {
21205
+ const inputType = field.ui?.inputType;
21206
+ if (inputType === "number") {
21207
+ const constraints2 = commonConstraints(required, { kind: "numberType" });
21208
+ if (typeof field.ui?.min === "number") constraints2.push({ kind: "numberMin", minimum: field.ui.min });
21209
+ if (typeof field.ui?.max === "number") constraints2.push({ kind: "numberMax", maximum: field.ui.max });
21210
+ return makePlan("number", "number", field, path21, required, constraints2);
21211
+ }
21212
+ const constraints = commonConstraints(required, { kind: "textType" });
21213
+ if (inputType === "email") constraints.push({ kind: "emailFormat" });
21214
+ if (inputType === "tel") constraints.push({ kind: "phoneFormat" });
21215
+ if (field.ui?.pattern) constraints.push({ kind: "patternFormat", pattern: field.ui.pattern });
21216
+ if (typeof field.maxLength === "number") constraints.push({ kind: "maxLength", maximum: field.maxLength });
21217
+ return makePlan("string", "string", field, path21, required, constraints);
21218
+ }
21219
+ case "number": {
21220
+ const constraints = commonConstraints(required, { kind: "numberType" });
21221
+ if (typeof field.min === "number") constraints.push({ kind: "numberMin", minimum: field.min });
21222
+ if (typeof field.max === "number") constraints.push({ kind: "numberMax", maximum: field.max });
21223
+ return makePlan("number", "number", field, path21, required, constraints);
21224
+ }
21225
+ case "slug": {
21226
+ const constraints = commonConstraints(required, { kind: "textType" }, { kind: "slugFormat" });
21227
+ if (typeof field.maxLength === "number") constraints.push({ kind: "maxLength", maximum: field.maxLength });
21228
+ return makePlan("string", "string", field, path21, required, constraints);
21229
+ }
21230
+ case "url":
21231
+ return makePlan("string", "string", field, path21, required, commonConstraints(
21232
+ required,
21233
+ { kind: "textType" },
21234
+ { kind: "urlFormat", allowRelative: Boolean(field.allowRelative) }
21235
+ ));
21236
+ case "reference":
21237
+ return makePlan("string", "string", field, path21, required, commonConstraints(required, { kind: "referenceText" }));
21238
+ case "date":
21239
+ case "time":
21240
+ case "datetime":
21241
+ return makePlan("string", "string", field, path21, required, commonConstraints(required, { kind: "textType" }));
21242
+ case "select":
21243
+ return makePlan("select", "select", field, path21, required, commonConstraints(required, {
21244
+ kind: "selectOption",
21245
+ source: resolveSelectConstraintSource(field),
21246
+ multiple: Boolean(field.multiple)
21247
+ }));
21248
+ case "richText":
21249
+ return makePlan("richText", "object", field, path21, required, commonConstraints(required, { kind: "richTextDoc" }));
21250
+ case "media":
21251
+ return makePlan("media", "object", field, path21, required, commonConstraints(required, { kind: "mediaObject" }));
21252
+ case "link":
21253
+ return makePlan("link", "object", field, path21, required, commonConstraints(required, { kind: "linkObject" }));
21254
+ case "boolean":
21255
+ return makePlan("boolean", "boolean", field, path21, required, requiredConstraints(required));
21256
+ case "repeater":
21257
+ return {
21258
+ ...makePlan("repeater", "array", field, path21, required, commonConstraints(required, { kind: "arrayType" })),
21259
+ ...field.polymorphic && field.itemTypes ? { repeatedItemVariants: deriveRepeatedItemVariants(field, [...path21, 0]) } : { repeatedItemPlan: deriveNestedFieldPlans(getRepeaterFields(field), [...path21, 0]) },
21260
+ constraints: withRepeaterItemConstraints(field, commonConstraints(required, { kind: "arrayType" }))
21261
+ };
21262
+ case "group":
21263
+ case "modal":
21264
+ return {
21265
+ ...makePlan("group", "object", field, path21, required, requiredConstraints(required)),
21266
+ children: deriveNestedFieldPlans(field.schema?.fields ?? [], path21),
21267
+ allowNullChildren: field.type === "group" && field.ui?.widget === "locationPicker"
21268
+ };
21269
+ case "tabGroup":
21270
+ return {
21271
+ ...makePlan("group", "object", field, path21, required, requiredConstraints(required)),
21272
+ children: field.tabs.flatMap((tab) => deriveNestedFieldPlans(tab.fields, path21))
21273
+ };
21274
+ case "presetOrCustom":
21275
+ case "contentTypeSelect":
21276
+ case "entryPicker":
21277
+ return makePlan("passthrough", "unknown", field, path21, false, []);
21278
+ default:
21279
+ return assertNever2(field);
21464
21280
  }
21465
- return schema;
21466
21281
  }
21467
- function buildRepeaterSchema(field, allowNull, allowIncomplete) {
21468
- const parsed = fieldSchema.array().parse(field.schema?.fields ?? []);
21469
- const childShape = {};
21470
- for (const child of parsed) {
21282
+ function commonConstraints(required, ...specific) {
21283
+ return required ? [{ kind: "required" }, ...specific] : [...specific];
21284
+ }
21285
+ function requiredConstraints(required) {
21286
+ return required ? [{ kind: "required" }] : [];
21287
+ }
21288
+ function makePlan(kind, valueKind, field, path21, required, constraints) {
21289
+ return {
21290
+ kind,
21291
+ valueKind,
21292
+ fieldId: field.id,
21293
+ label: field.label,
21294
+ fieldType: field.type,
21295
+ path: path21,
21296
+ required,
21297
+ constraints
21298
+ };
21299
+ }
21300
+ function withRepeaterItemConstraints(field, constraints) {
21301
+ const next = [...constraints];
21302
+ if (typeof field.minItems === "number") next.push({ kind: "minItems", minimum: field.minItems });
21303
+ if (typeof field.maxItems === "number") next.push({ kind: "maxItems", maximum: field.maxItems });
21304
+ return next;
21305
+ }
21306
+ function deriveRepeatedItemVariants(field, parentPath) {
21307
+ return Object.entries(field.itemTypes ?? {}).map(([typeId, itemType]) => ({
21308
+ typeId,
21309
+ fields: deriveNestedFieldPlans(getFlattenedRepeaterFields(itemType.fields), parentPath)
21310
+ }));
21311
+ }
21312
+ function deriveNestedFieldPlans(fields3, parentPath) {
21313
+ return fields3.map((field) => deriveFieldValidationPlan(field, [...parentPath, field.id]));
21314
+ }
21315
+ function getRepeaterFields(field) {
21316
+ const fields3 = field.polymorphic && field.itemTypes ? Object.values(field.itemTypes).flatMap((itemType) => itemType.fields) : field.schema?.fields ?? [];
21317
+ return getFlattenedRepeaterFields(fields3);
21318
+ }
21319
+ function getFlattenedRepeaterFields(fields3) {
21320
+ return fields3.flatMap((child) => {
21471
21321
  if (child.type === "group" && child.ui?.flattenInRepeater) {
21472
- const groupParsed = fieldSchema.array().parse(child.schema?.fields ?? []);
21473
- for (const gc of groupParsed) {
21474
- childShape[gc.id] = buildFieldSchema(gc, allowNull, allowIncomplete);
21475
- }
21476
- } else {
21477
- childShape[child.id] = buildFieldSchema(child, allowNull, allowIncomplete);
21322
+ return child.schema?.fields ?? [];
21478
21323
  }
21324
+ return child;
21325
+ });
21326
+ }
21327
+ function resolveSelectConstraintSource(field) {
21328
+ const source = normalizeSelectSource(field);
21329
+ switch (source.kind) {
21330
+ case "static":
21331
+ return { kind: "static", values: field.options.map((option) => option.value) };
21332
+ case "site":
21333
+ case "sdk":
21334
+ return { kind: "runtime", source: source.kind };
21335
+ default:
21336
+ return assertNever2(source);
21337
+ }
21338
+ }
21339
+ function normalizeFieldValue(plan, value, ctx) {
21340
+ switch (plan.kind) {
21341
+ case "number":
21342
+ return normalizeNumberInput(value, ctx.allowNull);
21343
+ case "string":
21344
+ return acceptsOptionalBlankAsMissing(plan, value) ? void 0 : value;
21345
+ case "group":
21346
+ return normalizeObjectChildren(plan.children, value, childValidationContext(plan, ctx));
21347
+ case "repeater":
21348
+ return normalizeRepeaterItems(plan, value, ctx);
21349
+ case "boolean":
21350
+ case "richText":
21351
+ case "media":
21352
+ case "link":
21353
+ case "select":
21354
+ case "passthrough":
21355
+ return value;
21356
+ default:
21357
+ return assertNever2(plan);
21358
+ }
21359
+ }
21360
+ function normalizeObjectChildren(children, value, ctx) {
21361
+ if (!value || typeof value !== "object" || Array.isArray(value)) return value;
21362
+ const normalized = { ...value };
21363
+ for (const child of children) {
21364
+ normalized[child.fieldId] = normalizeFieldValue(child, normalized[child.fieldId], ctx);
21479
21365
  }
21480
- let schema = z.object(childShape).catchall(z.unknown()).passthrough();
21481
- let arraySchema = z.array(schema);
21482
- if (typeof field.minItems === "number") {
21483
- arraySchema = arraySchema.min(field.minItems);
21366
+ return normalized;
21367
+ }
21368
+ function normalizeRepeaterItems(plan, value, ctx) {
21369
+ if (!Array.isArray(value)) return value;
21370
+ return value.map((item) => {
21371
+ if (!item || typeof item !== "object" || Array.isArray(item)) return item;
21372
+ const itemRecord = item;
21373
+ const fields3 = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === itemRecord._type)?.fields : plan.repeatedItemPlan;
21374
+ if (!fields3) return item;
21375
+ return normalizeObjectChildren(fields3, itemRecord, ctx);
21376
+ });
21377
+ }
21378
+ function normalizeNumberInput(value, allowNull) {
21379
+ if (value === "") return void 0;
21380
+ if (typeof value === "string" && value.trim() !== "") return Number(value);
21381
+ return value;
21382
+ }
21383
+ function acceptsOptionalBlankAsMissing(plan, value) {
21384
+ if (plan.required || typeof value !== "string" || value.trim() !== "") return false;
21385
+ return plan.constraints.some(
21386
+ (constraint) => constraint.kind === "urlFormat" || constraint.kind === "slugFormat" || constraint.kind === "emailFormat" || constraint.kind === "phoneFormat" || constraint.kind === "patternFormat"
21387
+ );
21388
+ }
21389
+ function validateNormalizedFieldValue(plan, value, ctx) {
21390
+ if (value === null && (ctx.allowNull || acceptsOptionalNullValue(plan))) return [];
21391
+ if (isMissingForPlan(plan, value)) {
21392
+ return shouldRequireValue(plan, value, ctx) ? [requiredIssue(plan, value)] : [];
21393
+ }
21394
+ switch (plan.kind) {
21395
+ case "string":
21396
+ return validateStringPlan(plan, value);
21397
+ case "number":
21398
+ return validateNumberPlan(plan, value);
21399
+ case "boolean":
21400
+ return typeof value === "boolean" ? [] : [issue(plan, "wrongType", { expected: "a true/false value" })];
21401
+ case "select":
21402
+ return validateSelectPlan(plan, value);
21403
+ case "richText":
21404
+ return isValidRichTextDoc(value) ? [] : [issue(plan, "invalidRichText")];
21405
+ case "media":
21406
+ return isPlainObject(value) ? [] : [issue(plan, "invalidMedia")];
21407
+ case "link":
21408
+ return linkSchema.safeParse(value).success ? [] : [issue(plan, "invalidLink")];
21409
+ case "repeater":
21410
+ return validateRepeaterPlan(plan, value, ctx);
21411
+ case "group":
21412
+ return validateGroupPlan(plan, value, ctx);
21413
+ case "passthrough":
21414
+ return [];
21415
+ default:
21416
+ return assertNever2(plan);
21484
21417
  }
21485
- if (typeof field.maxItems === "number") {
21486
- arraySchema = arraySchema.max(field.maxItems);
21418
+ }
21419
+ function shouldRequireValue(plan, value, ctx) {
21420
+ if (!plan.required || ctx.allowIncomplete) return false;
21421
+ if (value === null && ctx.allowNull) return false;
21422
+ return true;
21423
+ }
21424
+ function acceptsOptionalNullValue(plan) {
21425
+ return !plan.required && (plan.kind === "media" || plan.kind === "number");
21426
+ }
21427
+ function isMissingForPlan(plan, value) {
21428
+ if (value === void 0) return true;
21429
+ if (value === null) return plan.required;
21430
+ if (typeof value === "string" && value.trim() === "") return true;
21431
+ if (Array.isArray(value) && value.length === 0) return true;
21432
+ return false;
21433
+ }
21434
+ function validateStringPlan(plan, value) {
21435
+ if (typeof value !== "string") return [issue(plan, "wrongType", { expected: "text" })];
21436
+ return plan.constraints.flatMap((constraint) => validateStringConstraint(plan, constraint, value));
21437
+ }
21438
+ function validateStringConstraint(plan, constraint, value) {
21439
+ switch (constraint.kind) {
21440
+ case "required":
21441
+ case "textType":
21442
+ case "referenceText":
21443
+ return [];
21444
+ case "maxLength":
21445
+ return value.length > constraint.maximum ? [issue(plan, "tooLong", { maximum: constraint.maximum })] : [];
21446
+ case "emailFormat":
21447
+ return z.email().safeParse(value).success ? [] : [issue(plan, "invalidEmail")];
21448
+ case "phoneFormat":
21449
+ return TEL_RE.test(value) ? [] : [issue(plan, "invalidPhone")];
21450
+ case "patternFormat": {
21451
+ const re = compilePattern(constraint.pattern);
21452
+ return re && !re.test(value) ? [issue(plan, "invalidPattern")] : [];
21453
+ }
21454
+ case "urlFormat":
21455
+ return isValidUrl(value, constraint.allowRelative) ? [] : [issue(plan, "invalidUrl")];
21456
+ case "slugFormat":
21457
+ return SLUG_PATTERN.test(value) ? [] : [issue(plan, "invalidSlug")];
21458
+ default:
21459
+ return assertNever2(constraint);
21487
21460
  }
21488
- return arraySchema;
21489
21461
  }
21490
- function buildGroupSchema(field, allowNull, allowIncomplete) {
21491
- const parsed = fieldSchema.array().parse(field.schema?.fields ?? []);
21492
- const childShape = {};
21493
- for (const child of parsed) {
21494
- childShape[child.id] = buildFieldSchema(child, allowNull, allowIncomplete);
21462
+ function validateNumberPlan(plan, value) {
21463
+ if (!isValidNumber(value)) return [issue(plan, "invalidNumber")];
21464
+ return plan.constraints.flatMap((constraint) => validateNumberConstraint(plan, constraint, Number(value)));
21465
+ }
21466
+ function validateNumberConstraint(plan, constraint, value) {
21467
+ switch (constraint.kind) {
21468
+ case "required":
21469
+ case "numberType":
21470
+ return [];
21471
+ case "numberMin":
21472
+ return value < constraint.minimum ? [issue(plan, "tooSmall", { minimum: constraint.minimum })] : [];
21473
+ case "numberMax":
21474
+ return value > constraint.maximum ? [issue(plan, "tooLarge", { maximum: constraint.maximum })] : [];
21475
+ default:
21476
+ return assertNever2(constraint);
21495
21477
  }
21496
- return z.object(childShape).catchall(z.unknown()).passthrough();
21497
21478
  }
21498
- function finalizeSchema(schema, required, allowNull, allowIncomplete = false) {
21499
- if (required) {
21500
- if (allowIncomplete) {
21501
- return allowNull ? schema.or(z.null()).optional() : schema.optional();
21479
+ function validateSelectPlan(plan, value) {
21480
+ const constraint = plan.constraints.find((candidate) => candidate.kind === "selectOption");
21481
+ if (!constraint || constraint.source.kind === "runtime") return [];
21482
+ return isValidSelectValue(value, constraint.source.values, constraint.multiple) ? [] : [issue(plan, "invalidOption")];
21483
+ }
21484
+ function validateRepeaterPlan(plan, value, ctx) {
21485
+ if (!Array.isArray(value)) return [issue(plan, "wrongType", { expected: "a list" })];
21486
+ const constraintIssues = plan.constraints.flatMap((constraint) => {
21487
+ switch (constraint.kind) {
21488
+ case "required":
21489
+ case "arrayType":
21490
+ return [];
21491
+ case "minItems":
21492
+ return value.length < constraint.minimum ? [issue(plan, "tooFewItems", { minimum: constraint.minimum, itemLabel: "item" })] : [];
21493
+ case "maxItems":
21494
+ return value.length > constraint.maximum ? [issue(plan, "tooManyItems", { maximum: constraint.maximum, itemLabel: "item" })] : [];
21495
+ default:
21496
+ return assertNever2(constraint);
21497
+ }
21498
+ });
21499
+ return [
21500
+ ...constraintIssues,
21501
+ ...value.flatMap((item, index) => validateRepeaterItem(plan, item, index, ctx))
21502
+ ];
21503
+ }
21504
+ function validateRepeaterItem(plan, item, index, ctx) {
21505
+ if (!isPlainObject(item)) return [];
21506
+ const itemType = item._type;
21507
+ const fields3 = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === itemType)?.fields : plan.repeatedItemPlan;
21508
+ if (!fields3) return [];
21509
+ return fields3.flatMap((childPlan) => {
21510
+ const indexedPlan = withIndexedPath(childPlan, plan.path, index);
21511
+ return validateNormalizedFieldValue(indexedPlan, item[childPlan.fieldId], ctx);
21512
+ });
21513
+ }
21514
+ function validateGroupPlan(plan, value, ctx) {
21515
+ if (!isPlainObject(value)) return [issue(plan, "wrongType", { expected: "an object" })];
21516
+ const childCtx = childValidationContext(plan, ctx);
21517
+ return plan.children.flatMap((childPlan) => validateNormalizedFieldValue(childPlan, value[childPlan.fieldId], childCtx));
21518
+ }
21519
+ function childValidationContext(plan, ctx) {
21520
+ return plan.allowNullChildren ? { ...ctx, allowNull: true } : ctx;
21521
+ }
21522
+ function withIndexedPath(plan, parentPath, index) {
21523
+ const suffix = plan.path.slice(parentPath.length + 1);
21524
+ return {
21525
+ ...plan,
21526
+ path: [...parentPath, index, plan.fieldId, ...suffix]
21527
+ };
21528
+ }
21529
+ function issue(plan, kind, extra) {
21530
+ return {
21531
+ kind,
21532
+ path: plan.path,
21533
+ fieldId: plan.fieldId,
21534
+ label: plan.label,
21535
+ ...extra ?? {}
21536
+ };
21537
+ }
21538
+ function requiredIssue(plan, value) {
21539
+ if (typeof value === "string") return issue(plan, "required", { emptyOrigin: "string" });
21540
+ if (Array.isArray(value)) return issue(plan, "required", { emptyOrigin: "array" });
21541
+ return issue(plan, "required");
21542
+ }
21543
+ function fieldPlanToZod(plan, options = {}) {
21544
+ const ctx = validationContext(options);
21545
+ return z.any().transform((value) => normalizeFieldValue(plan, value, ctx)).superRefine((value, zodCtx) => {
21546
+ for (const validationIssue of validateNormalizedFieldValue(plan, value, ctx)) {
21547
+ addFieldIssueToZodContext(zodCtx, validationIssue);
21502
21548
  }
21503
- return allowNull ? schema.or(z.null()) : schema;
21549
+ });
21550
+ }
21551
+ function addFieldIssueToZodContext(zodCtx, validationIssue) {
21552
+ const message = fieldIssueToMessage(validationIssue);
21553
+ switch (validationIssue.kind) {
21554
+ case "required":
21555
+ if (validationIssue.emptyOrigin) {
21556
+ zodCtx.addIssue({
21557
+ code: "too_small",
21558
+ origin: validationIssue.emptyOrigin,
21559
+ minimum: 1,
21560
+ inclusive: true,
21561
+ message
21562
+ });
21563
+ return;
21564
+ }
21565
+ zodCtx.addIssue({ code: "invalid_type", expected: "nonoptional", message });
21566
+ return;
21567
+ case "tooLong":
21568
+ zodCtx.addIssue({
21569
+ code: "too_big",
21570
+ origin: "string",
21571
+ maximum: validationIssue.maximum,
21572
+ inclusive: true,
21573
+ message
21574
+ });
21575
+ return;
21576
+ case "invalidOption":
21577
+ zodCtx.addIssue({ code: "invalid_value", values: [], message });
21578
+ return;
21579
+ case "wrongType":
21580
+ case "invalidEmail":
21581
+ case "invalidPhone":
21582
+ case "invalidPattern":
21583
+ case "invalidUrl":
21584
+ case "invalidNumber":
21585
+ case "tooSmall":
21586
+ case "tooLarge":
21587
+ case "invalidSlug":
21588
+ case "tooFewItems":
21589
+ case "tooManyItems":
21590
+ case "invalidRichText":
21591
+ case "invalidMedia":
21592
+ case "invalidLink":
21593
+ case "invalidReference":
21594
+ zodCtx.addIssue({ code: "custom", message });
21595
+ return;
21596
+ default:
21597
+ assertNever2(validationIssue);
21504
21598
  }
21505
- return allowNull ? schema.optional().nullable() : schema.optional();
21599
+ }
21600
+ function compilePattern(pattern) {
21601
+ try {
21602
+ return new RegExp(pattern);
21603
+ } catch {
21604
+ return null;
21605
+ }
21606
+ }
21607
+ function isValidNumber(value) {
21608
+ return typeof value === "number" && Number.isFinite(value);
21609
+ }
21610
+ function isValidUrl(value, allowRelative) {
21611
+ if (!value) return false;
21612
+ if (allowRelative && value.startsWith("/")) return true;
21613
+ try {
21614
+ const parsed = new URL(value);
21615
+ return ALLOWED_URL_PROTOCOLS.includes(parsed.protocol);
21616
+ } catch {
21617
+ return false;
21618
+ }
21619
+ }
21620
+ function isValidSelectValue(value, values, multiple) {
21621
+ if (multiple) {
21622
+ return Array.isArray(value) && value.every((item) => typeof item === "string" && values.includes(item));
21623
+ }
21624
+ return typeof value === "string" && values.includes(value);
21625
+ }
21626
+ function isEmptyValue(value) {
21627
+ if (value === null || value === void 0) return true;
21628
+ if (typeof value === "string" && value.trim() === "") return true;
21629
+ if (Array.isArray(value) && value.length === 0) return true;
21630
+ if (isEmptyRichTextDoc(value)) return true;
21631
+ return false;
21632
+ }
21633
+ function isPlainObject(value) {
21634
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
21635
+ }
21636
+ function isValidRichTextDoc(value) {
21637
+ const doc = richTextDoc(value);
21638
+ return Boolean(isPlainObject(doc) && doc.type === "doc");
21639
+ }
21640
+ function isEmptyRichTextDoc(value) {
21641
+ const doc = richTextDoc(value);
21642
+ if (!isPlainObject(doc)) return false;
21643
+ if (doc.type !== "doc") return false;
21644
+ const content = doc.content;
21645
+ if (!Array.isArray(content) || content.length === 0) return true;
21646
+ if (content.length !== 1) return false;
21647
+ const first = content[0];
21648
+ return first?.type === "paragraph" && (!Array.isArray(first.content) || first.content.length === 0);
21649
+ }
21650
+ function richTextDoc(value) {
21651
+ if (!isPlainObject(value)) return value;
21652
+ return isPlainObject(value.doc) ? value.doc : value;
21653
+ }
21654
+
21655
+ // ../blocks/src/system/manifest/registry.ts
21656
+ var REGISTRY_SYMBOL = /* @__PURE__ */ Symbol.for("@riverbankcms/blocks/manifest-registry");
21657
+ var isVitest = typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.VITEST === "true";
21658
+ var globalScope = globalThis;
21659
+ var manifestStore = isVitest ? /* @__PURE__ */ new Map() : globalScope[REGISTRY_SYMBOL] ??= /* @__PURE__ */ new Map();
21660
+ function registerManifest(manifest) {
21661
+ manifestStore.set(manifest.name, manifest);
21662
+ return manifest;
21663
+ }
21664
+ function preprocessEmptyValues(content) {
21665
+ if (!content || typeof content !== "object" || Array.isArray(content)) {
21666
+ return {};
21667
+ }
21668
+ const result = {};
21669
+ for (const [key, value] of Object.entries(content)) {
21670
+ result[key] = isEmptyValue(value) ? void 0 : value;
21671
+ }
21672
+ return result;
21673
+ }
21674
+ function createManifestValidator(manifestOrFields, options = {}) {
21675
+ const isDraft = options.mode === "draft";
21676
+ const shape = {};
21677
+ const fields3 = Array.isArray(manifestOrFields) ? manifestOrFields : manifestOrFields.fields ?? [];
21678
+ for (const field of fields3) {
21679
+ shape[field.id] = buildFieldSchema(field, options);
21680
+ }
21681
+ const baseSchema = z.object(shape).catchall(z.unknown()).passthrough();
21682
+ if (isDraft) {
21683
+ return z.preprocess(preprocessEmptyValues, baseSchema);
21684
+ }
21685
+ return baseSchema;
21686
+ }
21687
+ function buildFieldSchema(field, options) {
21688
+ return fieldPlanToZod(deriveFieldValidationPlan(field), options);
21506
21689
  }
21507
21690
 
21508
21691
  // ../theme-core/src/site-styles/types.ts
@@ -23465,8 +23648,8 @@ z.discriminatedUnion("kind", [
23465
23648
  }).strict()
23466
23649
  ]);
23467
23650
  function validateBlockSequence(snapshots, ctx) {
23468
- for (const issue of collectBlockSequenceIssues(snapshots)) {
23469
- ctx.addIssue(issue);
23651
+ for (const issue2 of collectBlockSequenceIssues(snapshots)) {
23652
+ ctx.addIssue(issue2);
23470
23653
  }
23471
23654
  }
23472
23655
  function collectBlockSequenceIssues(snapshots) {
@@ -24511,7 +24694,7 @@ var sectionStateToneTransform = {
24511
24694
  summary: "Map semantic section-state kind to feedback tone",
24512
24695
  run: (value) => sectionStateToneFromUnknown(value)
24513
24696
  };
24514
- function isPlainObject(value) {
24697
+ function isPlainObject2(value) {
24515
24698
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
24516
24699
  }
24517
24700
  function readString(obj, key) {
@@ -24557,7 +24740,7 @@ var heroContentWrapperClassTransform = {
24557
24740
  summary: "Resolve hero content wrapper className from alignment + max width controls",
24558
24741
  schema: heroContentWrapperClassSchema,
24559
24742
  run: (value, options, context) => {
24560
- const content = isPlainObject(value) ? value : isPlainObject(context.data) ? context.data : {};
24743
+ const content = isPlainObject2(value) ? value : isPlainObject2(context.data) ? context.data : {};
24561
24744
  const alignment = resolveHeroContentAlignment(content);
24562
24745
  const maxWidth = resolveHeroContentMaxWidth(content);
24563
24746
  const textAlignClass = alignment === "center" ? "rb-text-center" : alignment === "right" ? "rb-text-right" : "rb-text-left";
@@ -24576,7 +24759,7 @@ var heroCtaJustifyTransform = {
24576
24759
  kind: "string",
24577
24760
  summary: "Map hero contentAlignment to CTA row justify value",
24578
24761
  run: (value, _options, context) => {
24579
- const content = isPlainObject(value) ? value : isPlainObject(context.data) ? context.data : {};
24762
+ const content = isPlainObject2(value) ? value : isPlainObject2(context.data) ? context.data : {};
24580
24763
  const alignment = resolveHeroContentAlignment(content);
24581
24764
  return alignment === "left" ? "start" : alignment === "right" ? "end" : "center";
24582
24765
  }
@@ -24586,7 +24769,7 @@ var heroStackJustifyFromVerticalAlignmentTransform = {
24586
24769
  kind: "string",
24587
24770
  summary: "Map hero verticalAlignment to stack justify value",
24588
24771
  run: (value, _options, context) => {
24589
- const content = isPlainObject(value) ? value : isPlainObject(context.data) ? context.data : {};
24772
+ const content = isPlainObject2(value) ? value : isPlainObject2(context.data) ? context.data : {};
24590
24773
  const vertical = resolveHeroVerticalAlignment(content);
24591
24774
  return vertical === "top" ? "start" : vertical === "bottom" ? "end" : "center";
24592
24775
  }
@@ -24600,7 +24783,7 @@ var heroGridClassFromVerticalAlignmentTransform = {
24600
24783
  summary: "Append align-items class for hero split layouts based on verticalAlignment",
24601
24784
  schema: heroGridClassFromVerticalAlignmentSchema,
24602
24785
  run: (value, options, context) => {
24603
- const content = isPlainObject(value) ? value : isPlainObject(context.data) ? context.data : {};
24786
+ const content = isPlainObject2(value) ? value : isPlainObject2(context.data) ? context.data : {};
24604
24787
  const vertical = resolveHeroVerticalAlignment(content);
24605
24788
  const itemsClass = vertical === "top" ? "rb-items-start" : vertical === "bottom" ? "rb-items-end" : "rb-items-center";
24606
24789
  return joinClasses3([options.base, itemsClass]);
@@ -32014,6 +32197,71 @@ init_shadow2();
32014
32197
  // ../blocks/src/index.ts
32015
32198
  init_blocks();
32016
32199
  init_colorStyles();
32200
+ var Rfc6902PatchOp = z.discriminatedUnion("op", [
32201
+ // Standard RFC-6902 operations
32202
+ z.object({
32203
+ op: z.literal("add"),
32204
+ path: z.string(),
32205
+ value: z.unknown()
32206
+ }),
32207
+ z.object({
32208
+ op: z.literal("remove"),
32209
+ path: z.string()
32210
+ }),
32211
+ z.object({
32212
+ op: z.literal("replace"),
32213
+ path: z.string(),
32214
+ value: z.unknown()
32215
+ }),
32216
+ z.object({
32217
+ op: z.literal("move"),
32218
+ from: z.string(),
32219
+ path: z.string()
32220
+ }),
32221
+ z.object({
32222
+ op: z.literal("copy"),
32223
+ from: z.string(),
32224
+ path: z.string()
32225
+ }),
32226
+ // Block-level operations (Phase 2)
32227
+ z.object({
32228
+ op: z.literal("add_block"),
32229
+ blockKind: z.string(),
32230
+ afterBlockId: z.string().nullable(),
32231
+ content: z.record(z.string(), z.unknown()),
32232
+ rationale: z.string()
32233
+ }),
32234
+ z.object({
32235
+ op: z.literal("delete_block"),
32236
+ blockId: z.string(),
32237
+ rationale: z.string()
32238
+ }),
32239
+ z.object({
32240
+ op: z.literal("reorder_block"),
32241
+ blockId: z.string(),
32242
+ afterBlockId: z.string().nullable(),
32243
+ rationale: z.string()
32244
+ })
32245
+ ]);
32246
+ z.object({
32247
+ request: z.string().min(1).max(2e3)
32248
+ });
32249
+ var MultiPagePatchEnvelope = z.object({
32250
+ pageId: z.string(),
32251
+ blockId: z.string().optional(),
32252
+ // Not present for page-level ops
32253
+ blockKind: z.string().optional(),
32254
+ blockPurpose: z.string().optional().nullable(),
32255
+ ops: z.array(Rfc6902PatchOp),
32256
+ rationale: z.string(),
32257
+ currentContent: z.record(z.string(), z.unknown()).optional()
32258
+ });
32259
+ z.object({
32260
+ patches: z.array(MultiPagePatchEnvelope),
32261
+ assistantMessage: z.string(),
32262
+ pagesModified: z.number(),
32263
+ toolCallsUsed: z.number()
32264
+ });
32017
32265
 
32018
32266
  // src/types/block-kinds.ts
32019
32267
  var SYSTEM_BLOCK_KINDS = [
@@ -32242,7 +32490,7 @@ var fieldBindingSchema = z.object({
32242
32490
  var metaBindingSchema = z.object({
32243
32491
  source: z.literal("meta"),
32244
32492
  path: z.enum(TEMPLATE_BINDING_META_PATHS, {
32245
- error: (issue) => describeInvalidTemplateBindingMetaPath(String(issue.input))
32493
+ error: (issue2) => describeInvalidTemplateBindingMetaPath(String(issue2.input))
32246
32494
  })
32247
32495
  });
32248
32496
  var literalBindingSchema = z.object({
@@ -32253,7 +32501,7 @@ var transformBindingSchema = z.object({
32253
32501
  source: z.literal("transform"),
32254
32502
  path: z.string().min(1, "Transform path is required"),
32255
32503
  transform: z.enum(BINDING_TRANSFORM_VALUES, {
32256
- error: (issue) => describeInvalidBindingTransform(String(issue.input))
32504
+ error: (issue2) => describeInvalidBindingTransform(String(issue2.input))
32257
32505
  })
32258
32506
  });
32259
32507
  var baseBindingSchema = z.discriminatedUnion("source", [
@@ -34659,9 +34907,9 @@ async function pushConfigAction(output, options) {
34659
34907
  const parseResult = riverbankSiteConfigSchema.safeParse(rawConfig);
34660
34908
  if (!parseResult.success) {
34661
34909
  output.error("Invalid config", {
34662
- issues: parseResult.error.issues.map((issue) => ({
34663
- path: issue.path.join("."),
34664
- message: issue.message
34910
+ issues: parseResult.error.issues.map((issue2) => ({
34911
+ path: issue2.path.join("."),
34912
+ message: issue2.message
34665
34913
  }))
34666
34914
  });
34667
34915
  }
@@ -35595,7 +35843,7 @@ async function readSettings(contentDir) {
35595
35843
  if (!parsed.success) {
35596
35844
  throw new Error(
35597
35845
  `${filePath}: invalid settings.json
35598
- ` + parsed.error.issues.map((issue) => ` - ${issue.path.join(".") || "(root)"}: ${issue.message}`).join("\n")
35846
+ ` + parsed.error.issues.map((issue2) => ` - ${issue2.path.join(".") || "(root)"}: ${issue2.message}`).join("\n")
35599
35847
  );
35600
35848
  }
35601
35849
  return parsed.data;
@@ -38436,15 +38684,15 @@ function validateBlockContent(data, manifest, customFields) {
38436
38684
  validator.parse(data);
38437
38685
  } catch (error) {
38438
38686
  if (error instanceof ZodError) {
38439
- for (const issue of error.issues) {
38440
- const fieldPath = issue.path.join(".");
38441
- if (issue.code === "invalid_type" && issue.received === "undefined") {
38687
+ for (const issue2 of error.issues) {
38688
+ const fieldPath = issue2.path.join(".");
38689
+ if (issue2.code === "invalid_type" && issue2.received === "undefined") {
38442
38690
  continue;
38443
38691
  }
38444
38692
  warnings.push({
38445
38693
  type: "invalid_content",
38446
38694
  field: fieldPath || "(root)",
38447
- message: `Invalid content${fieldPath ? ` in "${fieldPath}"` : ""}: ${issue.message}`
38695
+ message: `Invalid content${fieldPath ? ` in "${fieldPath}"` : ""}: ${issue2.message}`
38448
38696
  });
38449
38697
  }
38450
38698
  }
@@ -41309,7 +41557,7 @@ async function handleThemePushForAllScope(params) {
41309
41557
  }
41310
41558
  const validation = themeSchema2.safeParse(localTheme.theme);
41311
41559
  if (!validation.success) {
41312
- const errors = validation.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
41560
+ const errors = validation.error.issues.map((issue2) => ` - ${issue2.path.join(".")}: ${issue2.message}`).join("\n");
41313
41561
  output.warn(
41314
41562
  `Skipping theme push - invalid theme at content/themes/${themeName}.json:
41315
41563
  ${errors}`
@@ -42022,7 +42270,7 @@ ${available.map((t) => ` - ${t.name} (content/themes/${t.name}.json)`).join("\n
42022
42270
  }
42023
42271
  const validation = themeSchema.safeParse(localTheme.theme);
42024
42272
  if (!validation.success) {
42025
- const errors = validation.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
42273
+ const errors = validation.error.issues.map((issue2) => ` - ${issue2.path.join(".")}: ${issue2.message}`).join("\n");
42026
42274
  output.error(
42027
42275
  `Invalid theme file at content/themes/${themeName}.json:
42028
42276
  ${errors}`
@@ -45110,7 +45358,7 @@ var SimpleCache = class {
45110
45358
  };
45111
45359
 
45112
45360
  // src/version.ts
45113
- var SDK_VERSION = "0.60.7";
45361
+ var SDK_VERSION = "0.60.9";
45114
45362
 
45115
45363
  // src/client/error.ts
45116
45364
  var RiverbankApiError = class _RiverbankApiError extends Error {
@@ -46913,9 +47161,9 @@ Examples:
46913
47161
  const parseResult = verifyConfigSchema.safeParse(rawConfig);
46914
47162
  if (!parseResult.success) {
46915
47163
  return output.error("Invalid config format", {
46916
- issues: parseResult.error.issues.map((issue) => ({
46917
- path: issue.path.join("."),
46918
- message: issue.message
47164
+ issues: parseResult.error.issues.map((issue2) => ({
47165
+ path: issue2.path.join("."),
47166
+ message: issue2.message
46919
47167
  })),
46920
47168
  suggestion: "Ensure your config exports a valid riverbank config object"
46921
47169
  });
@@ -47443,8 +47691,8 @@ async function executeAuditForEnv(output, target, options, isJsonOutput) {
47443
47691
  client
47444
47692
  });
47445
47693
  const mediaIssues = mapMediaGuardDiagnosticsToAuditIssues(mediaGuardResult.diagnostics);
47446
- errors.push(...mediaIssues.filter((issue) => issue.severity === "error"));
47447
- warnings.push(...mediaIssues.filter((issue) => issue.severity === "warning"));
47694
+ errors.push(...mediaIssues.filter((issue2) => issue2.severity === "error"));
47695
+ warnings.push(...mediaIssues.filter((issue2) => issue2.severity === "warning"));
47448
47696
  }
47449
47697
  if (!isJsonOutput) {
47450
47698
  output.info("Checking for duplicate page paths...");
@@ -47468,8 +47716,8 @@ async function executeAuditForEnv(output, target, options, isJsonOutput) {
47468
47716
  }
47469
47717
  errors.push(...auditEventReferences(localContent.events, auditCtx));
47470
47718
  const courseRefIssues = auditCourseReferences(localContent.courses, auditCtx);
47471
- errors.push(...courseRefIssues.filter((issue) => issue.severity === "error"));
47472
- warnings.push(...courseRefIssues.filter((issue) => issue.severity === "warning"));
47719
+ errors.push(...courseRefIssues.filter((issue2) => issue2.severity === "error"));
47720
+ warnings.push(...courseRefIssues.filter((issue2) => issue2.severity === "warning"));
47473
47721
  const success = errors.length === 0;
47474
47722
  const result = { success, errors, warnings };
47475
47723
  if (isJsonOutput) {
@@ -47484,14 +47732,14 @@ async function executeAuditForEnv(output, target, options, isJsonOutput) {
47484
47732
  output.info("");
47485
47733
  if (errors.length > 0) {
47486
47734
  output.warn(`Found ${errors.length} error(s):`);
47487
- for (const issue of errors) {
47488
- output.info(` [ERROR] ${issue.message}`);
47735
+ for (const issue2 of errors) {
47736
+ output.info(` [ERROR] ${issue2.message}`);
47489
47737
  }
47490
47738
  }
47491
47739
  if (warnings.length > 0) {
47492
47740
  output.warn(`Found ${warnings.length} warning(s):`);
47493
- for (const issue of warnings) {
47494
- output.info(` [WARN] ${issue.message}`);
47741
+ for (const issue2 of warnings) {
47742
+ output.info(` [WARN] ${issue2.message}`);
47495
47743
  }
47496
47744
  }
47497
47745
  if (success && warnings.length === 0) {
@@ -51523,7 +51771,7 @@ Examples:
51523
51771
  $ riverbankcms course delete beginner-yoga --yes
51524
51772
  `
51525
51773
  ).addCommand(listCommand7).addCommand(getCommand6).addCommand(upsertCommand6).addCommand(cancelCommand2).addCommand(deleteCommand3);
51526
- function isPlainObject2(value) {
51774
+ function isPlainObject3(value) {
51527
51775
  return !!value && typeof value === "object" && !Array.isArray(value);
51528
51776
  }
51529
51777
  function migrateLegacyEntryTitleInPlace(entry) {
@@ -51531,7 +51779,7 @@ function migrateLegacyEntryTitleInPlace(entry) {
51531
51779
  const titleValue = entry.title;
51532
51780
  delete entry.title;
51533
51781
  let migratedToDataTitle = false;
51534
- if (typeof titleValue === "string" && isPlainObject2(entry.data)) {
51782
+ if (typeof titleValue === "string" && isPlainObject3(entry.data)) {
51535
51783
  const data = entry.data;
51536
51784
  if (!("title" in data)) {
51537
51785
  data.title = titleValue;
@@ -51557,7 +51805,7 @@ async function backupLegacyFile(contentDir, legacyFilePath, dryRun) {
51557
51805
  }
51558
51806
  async function migrateLegacyEntriesFile(contentDir, legacyFilePath, options) {
51559
51807
  const raw = await readJsonFile(legacyFilePath);
51560
- if (!isPlainObject2(raw)) {
51808
+ if (!isPlainObject3(raw)) {
51561
51809
  throw new Error(`Legacy entries file is not an object: ${legacyFilePath}`);
51562
51810
  }
51563
51811
  const contentType = raw.contentType;
@@ -51578,14 +51826,14 @@ async function migrateLegacyEntriesFile(contentDir, legacyFilePath, options) {
51578
51826
  let legacyTitlesMigrated = 0;
51579
51827
  let legacyTitleFilesUpdated = 0;
51580
51828
  for (const entryUnknown of entries) {
51581
- if (!isPlainObject2(entryUnknown)) {
51829
+ if (!isPlainObject3(entryUnknown)) {
51582
51830
  throw new Error(`Invalid legacy entry (expected object): ${legacyFilePath}`);
51583
51831
  }
51584
51832
  const identifier = entryUnknown.identifier;
51585
51833
  if (typeof identifier !== "string" || identifier.length === 0) {
51586
51834
  throw new Error(`Invalid legacy entry identifier in: ${legacyFilePath}`);
51587
51835
  }
51588
- if (!isPlainObject2(entryUnknown.data)) {
51836
+ if (!isPlainObject3(entryUnknown.data)) {
51589
51837
  throw new Error(`Invalid legacy entry data (expected object) for "${identifier}" in: ${legacyFilePath}`);
51590
51838
  }
51591
51839
  const { updated, migratedToDataTitle } = migrateLegacyEntryTitleInPlace(entryUnknown);
@@ -51620,7 +51868,7 @@ async function migrateLegacyTitlesInDirectory(typeDir, options) {
51620
51868
  for (const filePath of files) {
51621
51869
  scanned += 1;
51622
51870
  const raw = await readJsonFile(filePath);
51623
- if (!isPlainObject2(raw)) continue;
51871
+ if (!isPlainObject3(raw)) continue;
51624
51872
  const { updated, migratedToDataTitle } = migrateLegacyEntryTitleInPlace(raw);
51625
51873
  if (!updated) continue;
51626
51874
  updatedFiles += 1;