@riverbankcms/sdk 0.60.7 → 0.60.11

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 (44) hide show
  1. package/dist/_dts/api/src/index.d.ts +2 -1
  2. package/dist/_dts/api/src/navigation/linkValue.d.ts +15 -2
  3. package/dist/_dts/api/src/navigation.d.ts +2 -0
  4. package/dist/_dts/blocks/src/index.d.ts +2 -1
  5. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +185 -0
  6. package/dist/_dts/blocks/src/system/manifest/index.d.ts +1 -0
  7. package/dist/_dts/blocks/src/system/manifest/validation.d.ts +2 -84
  8. package/dist/_dts/blocks/src/system/node/fragments/ctaButton.d.ts +2 -2
  9. package/dist/_dts/blocks/src/system/runtime/nodes/basic.d.ts +2 -1
  10. package/dist/_dts/blocks/src/system/types/link.d.ts +100 -2
  11. package/dist/_dts/db/src/schemas/forms.d.ts +24 -3
  12. package/dist/_dts/editor-blocks/src/widgets/link/LinkSummary.d.ts +1 -1
  13. package/dist/_dts/editor-blocks/src/widgets/link/LinkWidget.d.ts +1 -1
  14. package/dist/_dts/editor-blocks/src/widgets/link/utils.d.ts +1 -1
  15. package/dist/_dts/sdk/src/config/typed-entries.d.ts +36 -1
  16. package/dist/_dts/sdk/src/contracts/content.d.ts +13 -2
  17. package/dist/_dts/sdk/src/contracts/index.d.ts +1 -1
  18. package/dist/_dts/sdk/src/contracts/system-block-content.d.ts +1 -0
  19. package/dist/_dts/sdk/src/version.d.ts +1 -1
  20. package/dist/_dts/theme-core/src/buttons/classNames.d.ts +21 -0
  21. package/dist/_dts/theme-core/src/buttons/index.d.ts +1 -0
  22. package/dist/_dts/theme-core/src/buttons/types.d.ts +1 -0
  23. package/dist/cli/index.mjs +736 -427
  24. package/dist/client/bookings.mjs +746 -149
  25. package/dist/client/client.mjs +40389 -39847
  26. package/dist/client/hooks.mjs +1737 -1735
  27. package/dist/client/rendering/client.mjs +351 -113
  28. package/dist/client/rendering/islands.mjs +4549 -4366
  29. package/dist/client/rendering.mjs +1915 -1373
  30. package/dist/preview-next/client/runtime.mjs +1509 -945
  31. package/dist/server/components.mjs +3148 -2781
  32. package/dist/server/config-validation.mjs +1727 -1725
  33. package/dist/server/config.mjs +1727 -1725
  34. package/dist/server/data.mjs +1737 -1735
  35. package/dist/server/index.mjs +1 -1
  36. package/dist/server/next.mjs +3193 -2826
  37. package/dist/server/page-converter.mjs +10 -10
  38. package/dist/server/prebuild.mjs +1 -1
  39. package/dist/server/rendering/server.mjs +3161 -2794
  40. package/dist/server/rendering.mjs +3161 -2794
  41. package/dist/server/routing.mjs +297 -314
  42. package/dist/server/server.mjs +1738 -1736
  43. package/package.json +1 -1
  44. package/dist/_dts/blocks/src/system/runtime/shared/themedButtonClass.d.ts +0 -11
@@ -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"() {
@@ -2300,6 +2291,13 @@ var init_personalities = __esm({
2300
2291
  }
2301
2292
  });
2302
2293
 
2294
+ // ../theme-core/src/buttons/classNames.ts
2295
+ var init_classNames = __esm({
2296
+ "../theme-core/src/buttons/classNames.ts"() {
2297
+ init_types2();
2298
+ }
2299
+ });
2300
+
2303
2301
  // ../theme-core/src/tokens/resolver.ts
2304
2302
  var init_resolver = __esm({
2305
2303
  "../theme-core/src/tokens/resolver.ts"() {
@@ -2418,6 +2416,7 @@ var init_generateDefaultButtonSystem = __esm({
2418
2416
  var init_generateButtonCss = __esm({
2419
2417
  "../theme-core/src/buttons/generateButtonCss.ts"() {
2420
2418
  init_types2();
2419
+ init_classNames();
2421
2420
  init_resolver();
2422
2421
  init_generateEffectsCSS();
2423
2422
  init_constants();
@@ -2448,6 +2447,7 @@ var init_core = __esm({
2448
2447
  var init_buttons = __esm({
2449
2448
  "../theme-core/src/buttons/index.ts"() {
2450
2449
  init_types2();
2450
+ init_classNames();
2451
2451
  init_generateButtonCss();
2452
2452
  init_generateDefaultButtonSystem();
2453
2453
  init_constants();
@@ -8330,9 +8330,9 @@ function createZodCodec(schema) {
8330
8330
  return decodeSuccess(parsed.data);
8331
8331
  }
8332
8332
  return decodeFailure(
8333
- parsed.error.issues.map((issue) => ({
8334
- path: zodIssuePath(issue.path),
8335
- message: issue.message
8333
+ parsed.error.issues.map((issue2) => ({
8334
+ path: zodIssuePath(issue2.path),
8335
+ message: issue2.message
8336
8336
  }))
8337
8337
  );
8338
8338
  }
@@ -8622,6 +8622,15 @@ var init_colorConversion = __esm({
8622
8622
  }
8623
8623
  });
8624
8624
 
8625
+ // ../blocks/src/lib/typeGuards.ts
8626
+ function isRecord2(value) {
8627
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8628
+ }
8629
+ var init_typeGuards = __esm({
8630
+ "../blocks/src/lib/typeGuards.ts"() {
8631
+ }
8632
+ });
8633
+
8625
8634
  // ../theme-core/src/responsiveDisplay.ts
8626
8635
  function mobileOnlyDisplay(display) {
8627
8636
  return `${BASE_DISPLAY_CLASSES[display]} rb-lg-hidden`;
@@ -11191,7 +11200,7 @@ var init_shared6 = __esm({
11191
11200
  });
11192
11201
 
11193
11202
  // ../blocks/src/system/runtime/api/creditProducts.ts
11194
- function assertNever3(value) {
11203
+ function assertNever4(value) {
11195
11204
  throw new Error(`Unhandled credit product case: ${String(value)}`);
11196
11205
  }
11197
11206
  function useCasesFromModules(modules) {
@@ -11224,7 +11233,7 @@ function getCreditProductCreditUnit(useCase) {
11224
11233
  perCreditSuffix: "credit"
11225
11234
  };
11226
11235
  default:
11227
- return assertNever3(useCase);
11236
+ return assertNever4(useCase);
11228
11237
  }
11229
11238
  }
11230
11239
  function deriveCreditProductLabels(products, options = {}) {
@@ -20792,11 +20801,11 @@ siteBannerSettingsSchema.superRefine((value, ctx) => {
20792
20801
  return;
20793
20802
  }
20794
20803
  const analysis = analyzeSiteBannerBody(value.body);
20795
- for (const issue of analysis.issues) {
20804
+ for (const issue2 of analysis.issues) {
20796
20805
  ctx.addIssue({
20797
20806
  code: z.ZodIssueCode.custom,
20798
20807
  path: ["body"],
20799
- message: siteBannerBodyIssueMessage(issue)
20808
+ message: siteBannerBodyIssueMessage(issue2)
20800
20809
  });
20801
20810
  }
20802
20811
  });
@@ -20830,8 +20839,8 @@ function analyzeSiteBannerBody(body) {
20830
20839
  issues
20831
20840
  };
20832
20841
  }
20833
- function siteBannerBodyIssueMessage(issue) {
20834
- switch (issue) {
20842
+ function siteBannerBodyIssueMessage(issue2) {
20843
+ switch (issue2) {
20835
20844
  case "empty":
20836
20845
  return "Banner message is required when the banner is enabled.";
20837
20846
  case "too-many-paragraphs":
@@ -21083,324 +21092,35 @@ function isNavigationMenuValidationItem(value) {
21083
21092
  if (!isRecord(value)) return false;
21084
21093
  return typeof value.label === "string";
21085
21094
  }
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
21095
  var nullableString = z.union([z.string().min(1), z.null()]);
21392
- var internalLinkSchema = z.object({
21096
+ var resolvedInternalLinkKeys = ["entityId", "entityType", "href", "title", "typeLabel"];
21097
+ var internalRouteOnlyLinkSchema = z.object({
21098
+ kind: z.literal("internal"),
21099
+ routeId: z.string().min(1)
21100
+ }).passthrough().superRefine((value, ctx) => {
21101
+ const hasResolvedMetadata = resolvedInternalLinkKeys.some((key) => value[key] != null);
21102
+ if (!hasResolvedMetadata) return;
21103
+ ctx.addIssue({
21104
+ code: "custom",
21105
+ message: "Internal links with resolved metadata must include entityId, entityType, href, title, and typeLabel"
21106
+ });
21107
+ });
21108
+ var internalResolvedLinkSchema = z.object({
21393
21109
  kind: z.literal("internal"),
21394
21110
  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(),
21111
+ entityId: z.string().min(1),
21112
+ entityType: z.enum(["page", "content"]),
21113
+ href: z.string().min(1),
21114
+ title: z.string().min(1),
21115
+ typeLabel: z.string().min(1),
21400
21116
  contentTypeKey: nullableString.optional(),
21401
21117
  contentTypeName: nullableString.optional(),
21402
21118
  updatedAt: nullableString.optional()
21403
21119
  }).passthrough();
21120
+ var internalLinkSchema = z.union([
21121
+ internalResolvedLinkSchema,
21122
+ internalRouteOnlyLinkSchema
21123
+ ]);
21404
21124
  var externalLinkSchema2 = z.object({
21405
21125
  kind: z.literal("external"),
21406
21126
  href: z.string().min(1)
@@ -21425,84 +21145,605 @@ var linkSchema = z.union([
21425
21145
  pageLinkSchema2,
21426
21146
  entryLinkSchema2
21427
21147
  ]);
21428
- function buildLinkSchema() {
21429
- return linkSchema;
21148
+
21149
+ // ../blocks/src/system/manifest/fieldValidation/index.ts
21150
+ init_schema2();
21151
+ var TEL_RE = /^[+()0-9\s-]{3,}$/;
21152
+ var SLUG_PATTERN = /^(?:[a-z0-9]+(?:-[a-z0-9]+)*)(?:\/(?:[a-z0-9]+(?:-[a-z0-9]+)*))*$/;
21153
+ var ALLOWED_URL_PROTOCOLS = ["http:", "https:"];
21154
+ function assertNever2(value) {
21155
+ throw new Error(`Unhandled field validation variant: ${JSON.stringify(value)}`);
21430
21156
  }
21431
- function assertNeverSelectSource2(value) {
21432
- throw new Error(`Unhandled select source: ${JSON.stringify(value)}`);
21157
+ function validationContext(options = {}) {
21158
+ const isDraft = options.mode === "draft";
21159
+ return {
21160
+ allowNull: options.allowNull ?? true,
21161
+ allowIncomplete: isDraft || (options.allowIncomplete ?? false)
21162
+ };
21163
+ }
21164
+ function formatFieldPath(path21) {
21165
+ return path21.map((segment) => String(segment)).join(".");
21166
+ }
21167
+ function fieldIssueToMessage(issue2) {
21168
+ switch (issue2.kind) {
21169
+ case "required":
21170
+ return "This field is required";
21171
+ case "wrongType":
21172
+ return `${issue2.label} needs ${issue2.expected}`;
21173
+ case "tooLong":
21174
+ return `Use ${issue2.maximum} characters or fewer`;
21175
+ case "invalidEmail":
21176
+ return "Enter a valid email address";
21177
+ case "invalidPhone":
21178
+ return "Enter a valid phone number";
21179
+ case "invalidPattern":
21180
+ return "Check the format and try again";
21181
+ case "invalidUrl":
21182
+ return "Enter a valid web address";
21183
+ case "invalidNumber":
21184
+ return `${issue2.label} needs a number`;
21185
+ case "tooSmall":
21186
+ return `${issue2.label} must be ${issue2.minimum} or more`;
21187
+ case "tooLarge":
21188
+ return `${issue2.label} must be ${issue2.maximum} or less`;
21189
+ case "invalidSlug":
21190
+ return `${issue2.label} can only use lowercase letters, numbers, and dashes`;
21191
+ case "invalidOption":
21192
+ return `Choose one of the available options for ${issue2.label}`;
21193
+ case "tooFewItems":
21194
+ return `${issue2.label} must have at least ${issue2.minimum} ${pluralize(issue2.itemLabel, issue2.minimum)}`;
21195
+ case "tooManyItems":
21196
+ return `${issue2.label} must have at most ${issue2.maximum} ${pluralize(issue2.itemLabel, issue2.maximum)}`;
21197
+ case "invalidRichText":
21198
+ return `${issue2.label} must be valid rich text`;
21199
+ case "invalidMedia":
21200
+ return `${issue2.label} must be a valid media item`;
21201
+ case "invalidLink":
21202
+ return `${issue2.label} must be a valid link`;
21203
+ case "invalidReference":
21204
+ return `${issue2.label} must reference a valid item`;
21205
+ default:
21206
+ return assertNever2(issue2);
21207
+ }
21208
+ }
21209
+ function pluralize(label, count) {
21210
+ if (count === 1) return label;
21211
+ return label.endsWith("s") ? label : `${label}s`;
21212
+ }
21213
+ function deriveFieldValidationPlan(field, path21 = [field.id]) {
21214
+ const required = Boolean(field.required);
21215
+ switch (field.type) {
21216
+ case "text": {
21217
+ const inputType = field.ui?.inputType;
21218
+ if (inputType === "number") {
21219
+ const constraints2 = commonConstraints(required, { kind: "numberType" });
21220
+ if (typeof field.ui?.min === "number") constraints2.push({ kind: "numberMin", minimum: field.ui.min });
21221
+ if (typeof field.ui?.max === "number") constraints2.push({ kind: "numberMax", maximum: field.ui.max });
21222
+ return makePlan("number", "number", field, path21, required, constraints2);
21223
+ }
21224
+ const constraints = commonConstraints(required, { kind: "textType" });
21225
+ if (inputType === "email") constraints.push({ kind: "emailFormat" });
21226
+ if (inputType === "tel") constraints.push({ kind: "phoneFormat" });
21227
+ if (field.ui?.pattern) constraints.push({ kind: "patternFormat", pattern: field.ui.pattern });
21228
+ if (typeof field.maxLength === "number") constraints.push({ kind: "maxLength", maximum: field.maxLength });
21229
+ return makePlan("string", "string", field, path21, required, constraints);
21230
+ }
21231
+ case "number": {
21232
+ const constraints = commonConstraints(required, { kind: "numberType" });
21233
+ if (typeof field.min === "number") constraints.push({ kind: "numberMin", minimum: field.min });
21234
+ if (typeof field.max === "number") constraints.push({ kind: "numberMax", maximum: field.max });
21235
+ return makePlan("number", "number", field, path21, required, constraints);
21236
+ }
21237
+ case "slug": {
21238
+ const constraints = commonConstraints(required, { kind: "textType" }, { kind: "slugFormat" });
21239
+ if (typeof field.maxLength === "number") constraints.push({ kind: "maxLength", maximum: field.maxLength });
21240
+ return makePlan("string", "string", field, path21, required, constraints);
21241
+ }
21242
+ case "url":
21243
+ return makePlan("string", "string", field, path21, required, commonConstraints(
21244
+ required,
21245
+ { kind: "textType" },
21246
+ { kind: "urlFormat", allowRelative: Boolean(field.allowRelative) }
21247
+ ));
21248
+ case "reference":
21249
+ return makePlan("string", "string", field, path21, required, commonConstraints(required, { kind: "referenceText" }));
21250
+ case "date":
21251
+ case "time":
21252
+ case "datetime":
21253
+ return makePlan("string", "string", field, path21, required, commonConstraints(required, { kind: "textType" }));
21254
+ case "select":
21255
+ return makePlan("select", "select", field, path21, required, commonConstraints(required, {
21256
+ kind: "selectOption",
21257
+ source: resolveSelectConstraintSource(field),
21258
+ multiple: Boolean(field.multiple)
21259
+ }));
21260
+ case "richText":
21261
+ return makePlan("richText", "object", field, path21, required, commonConstraints(required, { kind: "richTextDoc" }));
21262
+ case "media":
21263
+ return makePlan("media", "object", field, path21, required, commonConstraints(required, { kind: "mediaObject" }));
21264
+ case "link":
21265
+ return makePlan("link", "object", field, path21, required, commonConstraints(required, { kind: "linkObject" }));
21266
+ case "boolean":
21267
+ return makePlan("boolean", "boolean", field, path21, required, requiredConstraints(required));
21268
+ case "repeater":
21269
+ return {
21270
+ ...makePlan("repeater", "array", field, path21, required, commonConstraints(required, { kind: "arrayType" })),
21271
+ ...field.polymorphic && field.itemTypes ? { repeatedItemVariants: deriveRepeatedItemVariants(field, [...path21, 0]) } : { repeatedItemPlan: deriveNestedFieldPlans(getRepeaterFields(field), [...path21, 0]) },
21272
+ constraints: withRepeaterItemConstraints(field, commonConstraints(required, { kind: "arrayType" }))
21273
+ };
21274
+ case "group":
21275
+ case "modal":
21276
+ return {
21277
+ ...makePlan("group", "object", field, path21, required, requiredConstraints(required)),
21278
+ children: deriveNestedFieldPlans(field.schema?.fields ?? [], path21),
21279
+ allowNullChildren: field.type === "group" && field.ui?.widget === "locationPicker"
21280
+ };
21281
+ case "tabGroup":
21282
+ return {
21283
+ ...makePlan("group", "object", field, path21, required, requiredConstraints(required)),
21284
+ children: field.tabs.flatMap((tab) => deriveNestedFieldPlans(tab.fields, path21))
21285
+ };
21286
+ case "presetOrCustom":
21287
+ case "contentTypeSelect":
21288
+ case "entryPicker":
21289
+ return makePlan("passthrough", "unknown", field, path21, false, []);
21290
+ default:
21291
+ return assertNever2(field);
21292
+ }
21293
+ }
21294
+ function commonConstraints(required, ...specific) {
21295
+ return required ? [{ kind: "required" }, ...specific] : [...specific];
21296
+ }
21297
+ function requiredConstraints(required) {
21298
+ return required ? [{ kind: "required" }] : [];
21299
+ }
21300
+ function makePlan(kind, valueKind, field, path21, required, constraints) {
21301
+ return {
21302
+ kind,
21303
+ valueKind,
21304
+ fieldId: field.id,
21305
+ label: field.label,
21306
+ fieldType: field.type,
21307
+ path: path21,
21308
+ required,
21309
+ constraints
21310
+ };
21311
+ }
21312
+ function withRepeaterItemConstraints(field, constraints) {
21313
+ const next = [...constraints];
21314
+ if (typeof field.minItems === "number") next.push({ kind: "minItems", minimum: field.minItems });
21315
+ if (typeof field.maxItems === "number") next.push({ kind: "maxItems", maximum: field.maxItems });
21316
+ return next;
21433
21317
  }
21434
- function buildSelectSchema(field) {
21318
+ function deriveRepeatedItemVariants(field, parentPath) {
21319
+ return Object.entries(field.itemTypes ?? {}).map(([typeId, itemType]) => ({
21320
+ typeId,
21321
+ fields: deriveNestedFieldPlans(getFlattenedRepeaterFields(itemType.fields), parentPath)
21322
+ }));
21323
+ }
21324
+ function deriveNestedFieldPlans(fields3, parentPath) {
21325
+ return fields3.map((field) => deriveFieldValidationPlan(field, [...parentPath, field.id]));
21326
+ }
21327
+ function getRepeaterFields(field) {
21328
+ const fields3 = field.polymorphic && field.itemTypes ? Object.values(field.itemTypes).flatMap((itemType) => itemType.fields) : field.schema?.fields ?? [];
21329
+ return getFlattenedRepeaterFields(fields3);
21330
+ }
21331
+ function getFlattenedRepeaterFields(fields3) {
21332
+ return fields3.flatMap((child) => {
21333
+ if (child.type === "group" && child.ui?.flattenInRepeater) {
21334
+ return child.schema?.fields ?? [];
21335
+ }
21336
+ return child;
21337
+ });
21338
+ }
21339
+ function resolveSelectConstraintSource(field) {
21435
21340
  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
- }
21341
+ switch (source.kind) {
21342
+ case "static":
21343
+ return { kind: "static", values: field.options.map((option) => option.value) };
21344
+ case "site":
21345
+ case "sdk":
21346
+ return { kind: "runtime", source: source.kind };
21347
+ default:
21348
+ return assertNever2(source);
21349
+ }
21350
+ }
21351
+ function normalizeFieldValue(plan, value, ctx) {
21352
+ switch (plan.kind) {
21353
+ case "number":
21354
+ return normalizeNumberInput(value, ctx.allowNull);
21355
+ case "string":
21356
+ return acceptsOptionalBlankAsMissing(plan, value) ? void 0 : value;
21357
+ case "group":
21358
+ return normalizeObjectChildren(plan.children, value, childValidationContext(plan, ctx));
21359
+ case "repeater":
21360
+ return normalizeRepeaterItems(plan, value, ctx);
21361
+ case "boolean":
21362
+ case "richText":
21363
+ case "media":
21364
+ case "link":
21365
+ case "select":
21366
+ case "passthrough":
21367
+ return value;
21368
+ default:
21369
+ return assertNever2(plan);
21370
+ }
21371
+ }
21372
+ function normalizeObjectChildren(children, value, ctx) {
21373
+ if (!value || typeof value !== "object" || Array.isArray(value)) return value;
21374
+ const normalized = { ...value };
21375
+ for (const child of children) {
21376
+ normalized[child.fieldId] = normalizeFieldValue(child, normalized[child.fieldId], ctx);
21377
+ }
21378
+ return normalized;
21379
+ }
21380
+ function normalizeRepeaterItems(plan, value, ctx) {
21381
+ if (!Array.isArray(value)) return value;
21382
+ return value.map((item) => {
21383
+ if (!item || typeof item !== "object" || Array.isArray(item)) return item;
21384
+ const itemRecord = item;
21385
+ const fields3 = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === itemRecord._type)?.fields : plan.repeatedItemPlan;
21386
+ if (!fields3) return item;
21387
+ return normalizeObjectChildren(fields3, itemRecord, ctx);
21388
+ });
21389
+ }
21390
+ function normalizeNumberInput(value, _allowNull) {
21391
+ if (value === "") return void 0;
21392
+ if (typeof value === "string" && value.trim() !== "") return Number(value);
21393
+ return value;
21394
+ }
21395
+ function acceptsOptionalBlankAsMissing(plan, value) {
21396
+ if (plan.required || typeof value !== "string" || value.trim() !== "") return false;
21397
+ return plan.constraints.some(
21398
+ (constraint) => constraint.kind === "urlFormat" || constraint.kind === "slugFormat" || constraint.kind === "emailFormat" || constraint.kind === "phoneFormat" || constraint.kind === "patternFormat"
21399
+ );
21400
+ }
21401
+ function validateNormalizedFieldValue(plan, value, ctx) {
21402
+ if (value === null && (ctx.allowNull || acceptsOptionalNullValue(plan))) return [];
21403
+ if (isMissingForPlan(plan, value)) {
21404
+ return shouldRequireValue(plan, value, ctx) ? [requiredIssue(plan, value)] : [];
21405
+ }
21406
+ switch (plan.kind) {
21407
+ case "string":
21408
+ return validateStringPlan(plan, value);
21409
+ case "number":
21410
+ return validateNumberPlan(plan, value);
21411
+ case "boolean":
21412
+ return typeof value === "boolean" ? [] : [issue(plan, "wrongType", { expected: "a true/false value" })];
21413
+ case "select":
21414
+ return validateSelectPlan(plan, value);
21415
+ case "richText":
21416
+ return isValidRichTextDoc(value) ? [] : [issue(plan, "invalidRichText")];
21417
+ case "media":
21418
+ return isPlainObject(value) ? [] : [issue(plan, "invalidMedia")];
21419
+ case "link":
21420
+ return linkSchema.safeParse(value).success ? [] : [issue(plan, "invalidLink")];
21421
+ case "repeater":
21422
+ return validateRepeaterPlan(plan, value, ctx);
21423
+ case "group":
21424
+ return validateGroupPlan(plan, value, ctx);
21425
+ case "passthrough":
21426
+ return [];
21427
+ default:
21428
+ return assertNever2(plan);
21429
+ }
21430
+ }
21431
+ function shouldRequireValue(plan, value, ctx) {
21432
+ if (!plan.required || ctx.allowIncomplete) return false;
21433
+ if (value === null && ctx.allowNull) return false;
21434
+ return true;
21435
+ }
21436
+ function acceptsOptionalNullValue(plan) {
21437
+ return !plan.required && (plan.kind === "media" || plan.kind === "number");
21438
+ }
21439
+ function isMissingForPlan(plan, value) {
21440
+ if (value === void 0) return true;
21441
+ if (value === null) return plan.required;
21442
+ if (typeof value === "string" && value.trim() === "") return true;
21443
+ if (Array.isArray(value) && value.length === 0) return true;
21444
+ return false;
21445
+ }
21446
+ function validateStringPlan(plan, value) {
21447
+ if (typeof value !== "string") return [issue(plan, "wrongType", { expected: "text" })];
21448
+ return plan.constraints.flatMap((constraint) => validateStringConstraint(plan, constraint, value));
21449
+ }
21450
+ function validateStringConstraint(plan, constraint, value) {
21451
+ switch (constraint.kind) {
21452
+ case "required":
21453
+ case "textType":
21454
+ case "referenceText":
21455
+ return [];
21456
+ case "maxLength":
21457
+ return value.length > constraint.maximum ? [issue(plan, "tooLong", { maximum: constraint.maximum })] : [];
21458
+ case "emailFormat":
21459
+ return z.email().safeParse(value).success ? [] : [issue(plan, "invalidEmail")];
21460
+ case "phoneFormat":
21461
+ return TEL_RE.test(value) ? [] : [issue(plan, "invalidPhone")];
21462
+ case "patternFormat": {
21463
+ const re = compilePattern(constraint.pattern);
21464
+ return re && !re.test(value) ? [issue(plan, "invalidPattern")] : [];
21465
+ }
21466
+ case "urlFormat":
21467
+ return isValidUrl(value, constraint.allowRelative) ? [] : [issue(plan, "invalidUrl")];
21468
+ case "slugFormat":
21469
+ return SLUG_PATTERN.test(value) ? [] : [issue(plan, "invalidSlug")];
21470
+ default:
21471
+ return assertNever2(constraint);
21472
+ }
21473
+ }
21474
+ function validateNumberPlan(plan, value) {
21475
+ if (!isValidNumber(value)) return [issue(plan, "invalidNumber")];
21476
+ return plan.constraints.flatMap((constraint) => validateNumberConstraint(plan, constraint, Number(value)));
21477
+ }
21478
+ function validateNumberConstraint(plan, constraint, value) {
21479
+ switch (constraint.kind) {
21480
+ case "required":
21481
+ case "numberType":
21482
+ return [];
21483
+ case "numberMin":
21484
+ return value < constraint.minimum ? [issue(plan, "tooSmall", { minimum: constraint.minimum })] : [];
21485
+ case "numberMax":
21486
+ return value > constraint.maximum ? [issue(plan, "tooLarge", { maximum: constraint.maximum })] : [];
21487
+ default:
21488
+ return assertNever2(constraint);
21489
+ }
21490
+ }
21491
+ function validateSelectPlan(plan, value) {
21492
+ const constraint = plan.constraints.find((candidate) => candidate.kind === "selectOption");
21493
+ if (!constraint || constraint.source.kind === "runtime") return [];
21494
+ return isValidSelectValue(value, constraint.source.values, constraint.multiple) ? [] : [issue(plan, "invalidOption")];
21495
+ }
21496
+ function validateRepeaterPlan(plan, value, ctx) {
21497
+ if (!Array.isArray(value)) return [issue(plan, "wrongType", { expected: "a list" })];
21498
+ const constraintIssues = plan.constraints.flatMap((constraint) => {
21499
+ switch (constraint.kind) {
21500
+ case "required":
21501
+ case "arrayType":
21502
+ return [];
21503
+ case "minItems":
21504
+ return value.length < constraint.minimum ? [issue(plan, "tooFewItems", { minimum: constraint.minimum, itemLabel: "item" })] : [];
21505
+ case "maxItems":
21506
+ return value.length > constraint.maximum ? [issue(plan, "tooManyItems", { maximum: constraint.maximum, itemLabel: "item" })] : [];
21448
21507
  default:
21449
- return assertNeverSelectSource2(source);
21508
+ return assertNever2(constraint);
21450
21509
  }
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();
21455
- }
21456
- return field.required ? singleValueSchema : singleValueSchema.nullable();
21510
+ });
21511
+ return [
21512
+ ...constraintIssues,
21513
+ ...value.flatMap((item, index) => validateRepeaterItem(plan, item, index, ctx))
21514
+ ];
21457
21515
  }
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`
21516
+ function validateRepeaterItem(plan, item, index, ctx) {
21517
+ if (!isPlainObject(item)) return [];
21518
+ const itemType = item._type;
21519
+ const fields3 = plan.repeatedItemVariants ? plan.repeatedItemVariants.find((variant) => variant.typeId === itemType)?.fields : plan.repeatedItemPlan;
21520
+ if (!fields3) return [];
21521
+ return fields3.flatMap((childPlan) => {
21522
+ const indexedPlan = materializeRepeaterItemPlan(childPlan, plan.path, index);
21523
+ return validateNormalizedFieldValue(indexedPlan, item[childPlan.fieldId], ctx);
21461
21524
  });
21462
- if (field.required) {
21463
- schema = schema.min(1, { message: `${field.label} is required` });
21525
+ }
21526
+ function validateGroupPlan(plan, value, ctx) {
21527
+ if (!isPlainObject(value)) return [issue(plan, "wrongType", { expected: "an object" })];
21528
+ const childCtx = childValidationContext(plan, ctx);
21529
+ return plan.children.flatMap((childPlan) => validateNormalizedFieldValue(childPlan, value[childPlan.fieldId], childCtx));
21530
+ }
21531
+ function childValidationContext(plan, ctx) {
21532
+ return plan.allowNullChildren ? { ...ctx, allowNull: true } : ctx;
21533
+ }
21534
+ function materializeRepeaterItemPlan(plan, parentPath, index) {
21535
+ return rebaseFieldPlanPath(plan, [...parentPath, 0], [...parentPath, index]);
21536
+ }
21537
+ function rebaseFieldPlanPath(plan, fromPrefix, toPrefix) {
21538
+ const path21 = rebaseFieldPath(plan.path, fromPrefix, toPrefix);
21539
+ switch (plan.kind) {
21540
+ case "group":
21541
+ return {
21542
+ ...plan,
21543
+ path: path21,
21544
+ children: plan.children.map((childPlan) => rebaseFieldPlanPath(childPlan, fromPrefix, toPrefix))
21545
+ };
21546
+ case "repeater":
21547
+ return {
21548
+ ...plan,
21549
+ path: path21,
21550
+ ...plan.repeatedItemVariants ? {
21551
+ repeatedItemVariants: plan.repeatedItemVariants.map((variant) => ({
21552
+ ...variant,
21553
+ fields: variant.fields.map((fieldPlan) => rebaseFieldPlanPath(fieldPlan, fromPrefix, toPrefix))
21554
+ }))
21555
+ } : {},
21556
+ ...plan.repeatedItemPlan ? {
21557
+ repeatedItemPlan: plan.repeatedItemPlan.map(
21558
+ (fieldPlan) => rebaseFieldPlanPath(fieldPlan, fromPrefix, toPrefix)
21559
+ )
21560
+ } : {}
21561
+ };
21562
+ case "string":
21563
+ case "number":
21564
+ case "boolean":
21565
+ case "richText":
21566
+ case "media":
21567
+ case "link":
21568
+ case "select":
21569
+ case "passthrough":
21570
+ return {
21571
+ ...plan,
21572
+ path: path21
21573
+ };
21574
+ default:
21575
+ return assertNever2(plan);
21464
21576
  }
21465
- return schema;
21466
21577
  }
21467
- function buildRepeaterSchema(field, allowNull, allowIncomplete) {
21468
- const parsed = fieldSchema.array().parse(field.schema?.fields ?? []);
21469
- const childShape = {};
21470
- for (const child of parsed) {
21471
- 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);
21578
+ function rebaseFieldPath(path21, fromPrefix, toPrefix) {
21579
+ if (!startsWithFieldPath(path21, fromPrefix)) {
21580
+ throw new Error(`Cannot rebase field path ${formatFieldPath(path21)} from ${formatFieldPath(fromPrefix)}`);
21581
+ }
21582
+ return [...toPrefix, ...path21.slice(fromPrefix.length)];
21583
+ }
21584
+ function startsWithFieldPath(path21, prefix) {
21585
+ return prefix.every((segment, index) => path21[index] === segment);
21586
+ }
21587
+ function issue(plan, kind, extra) {
21588
+ return {
21589
+ kind,
21590
+ path: plan.path,
21591
+ fieldId: plan.fieldId,
21592
+ label: plan.label,
21593
+ ...extra ?? {}
21594
+ };
21595
+ }
21596
+ function requiredIssue(plan, value) {
21597
+ if (typeof value === "string") return issue(plan, "required", { emptyOrigin: "string" });
21598
+ if (Array.isArray(value)) return issue(plan, "required", { emptyOrigin: "array" });
21599
+ return issue(plan, "required");
21600
+ }
21601
+ function fieldPlanToZod(plan, options = {}) {
21602
+ const ctx = validationContext(options);
21603
+ return z.any().transform((value) => normalizeFieldValue(plan, value, ctx)).superRefine((value, zodCtx) => {
21604
+ for (const validationIssue of validateNormalizedFieldValue(plan, value, ctx)) {
21605
+ addFieldIssueToZodContext(zodCtx, validationIssue);
21478
21606
  }
21607
+ });
21608
+ }
21609
+ function addFieldIssueToZodContext(zodCtx, validationIssue) {
21610
+ const message = fieldIssueToMessage(validationIssue);
21611
+ switch (validationIssue.kind) {
21612
+ case "required":
21613
+ if (validationIssue.emptyOrigin) {
21614
+ zodCtx.addIssue({
21615
+ code: "too_small",
21616
+ origin: validationIssue.emptyOrigin,
21617
+ minimum: 1,
21618
+ inclusive: true,
21619
+ message
21620
+ });
21621
+ return;
21622
+ }
21623
+ zodCtx.addIssue({ code: "invalid_type", expected: "nonoptional", message });
21624
+ return;
21625
+ case "tooLong":
21626
+ zodCtx.addIssue({
21627
+ code: "too_big",
21628
+ origin: "string",
21629
+ maximum: validationIssue.maximum,
21630
+ inclusive: true,
21631
+ message
21632
+ });
21633
+ return;
21634
+ case "invalidOption":
21635
+ zodCtx.addIssue({ code: "invalid_value", values: [], message });
21636
+ return;
21637
+ case "wrongType":
21638
+ case "invalidEmail":
21639
+ case "invalidPhone":
21640
+ case "invalidPattern":
21641
+ case "invalidUrl":
21642
+ case "invalidNumber":
21643
+ case "tooSmall":
21644
+ case "tooLarge":
21645
+ case "invalidSlug":
21646
+ case "tooFewItems":
21647
+ case "tooManyItems":
21648
+ case "invalidRichText":
21649
+ case "invalidMedia":
21650
+ case "invalidLink":
21651
+ case "invalidReference":
21652
+ zodCtx.addIssue({ code: "custom", message });
21653
+ return;
21654
+ default:
21655
+ assertNever2(validationIssue);
21479
21656
  }
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);
21657
+ }
21658
+ function compilePattern(pattern) {
21659
+ try {
21660
+ return new RegExp(pattern);
21661
+ } catch {
21662
+ return null;
21484
21663
  }
21485
- if (typeof field.maxItems === "number") {
21486
- arraySchema = arraySchema.max(field.maxItems);
21664
+ }
21665
+ function isValidNumber(value) {
21666
+ return typeof value === "number" && Number.isFinite(value);
21667
+ }
21668
+ function isValidUrl(value, allowRelative) {
21669
+ if (!value) return false;
21670
+ if (allowRelative && value.startsWith("/")) return true;
21671
+ try {
21672
+ const parsed = new URL(value);
21673
+ return ALLOWED_URL_PROTOCOLS.includes(parsed.protocol);
21674
+ } catch {
21675
+ return false;
21487
21676
  }
21488
- return arraySchema;
21489
21677
  }
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);
21678
+ function isValidSelectValue(value, values, multiple) {
21679
+ if (multiple) {
21680
+ return Array.isArray(value) && value.every((item) => typeof item === "string" && values.includes(item));
21495
21681
  }
21496
- return z.object(childShape).catchall(z.unknown()).passthrough();
21682
+ return typeof value === "string" && values.includes(value);
21497
21683
  }
21498
- function finalizeSchema(schema, required, allowNull, allowIncomplete = false) {
21499
- if (required) {
21500
- if (allowIncomplete) {
21501
- return allowNull ? schema.or(z.null()).optional() : schema.optional();
21502
- }
21503
- return allowNull ? schema.or(z.null()) : schema;
21684
+ function isEmptyValue(value) {
21685
+ if (value === null || value === void 0) return true;
21686
+ if (typeof value === "string" && value.trim() === "") return true;
21687
+ if (Array.isArray(value) && value.length === 0) return true;
21688
+ if (isEmptyRichTextDoc(value)) return true;
21689
+ return false;
21690
+ }
21691
+ function isPlainObject(value) {
21692
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
21693
+ }
21694
+ function isValidRichTextDoc(value) {
21695
+ const doc = richTextDoc(value);
21696
+ return Boolean(isPlainObject(doc) && doc.type === "doc");
21697
+ }
21698
+ function isEmptyRichTextDoc(value) {
21699
+ const doc = richTextDoc(value);
21700
+ if (!isPlainObject(doc)) return false;
21701
+ if (doc.type !== "doc") return false;
21702
+ const content = doc.content;
21703
+ if (!Array.isArray(content) || content.length === 0) return true;
21704
+ if (content.length !== 1) return false;
21705
+ const first = content[0];
21706
+ return first?.type === "paragraph" && (!Array.isArray(first.content) || first.content.length === 0);
21707
+ }
21708
+ function richTextDoc(value) {
21709
+ if (!isPlainObject(value)) return value;
21710
+ return isPlainObject(value.doc) ? value.doc : value;
21711
+ }
21712
+
21713
+ // ../blocks/src/system/manifest/registry.ts
21714
+ var REGISTRY_SYMBOL = /* @__PURE__ */ Symbol.for("@riverbankcms/blocks/manifest-registry");
21715
+ var isVitest = typeof process !== "undefined" && typeof process.env !== "undefined" && process.env.VITEST === "true";
21716
+ var globalScope = globalThis;
21717
+ var manifestStore = isVitest ? /* @__PURE__ */ new Map() : globalScope[REGISTRY_SYMBOL] ??= /* @__PURE__ */ new Map();
21718
+ function registerManifest(manifest) {
21719
+ manifestStore.set(manifest.name, manifest);
21720
+ return manifest;
21721
+ }
21722
+ function preprocessEmptyValues(content) {
21723
+ if (!content || typeof content !== "object" || Array.isArray(content)) {
21724
+ return {};
21725
+ }
21726
+ const result = {};
21727
+ for (const [key, value] of Object.entries(content)) {
21728
+ result[key] = isEmptyValue(value) ? void 0 : value;
21504
21729
  }
21505
- return allowNull ? schema.optional().nullable() : schema.optional();
21730
+ return result;
21731
+ }
21732
+ function createManifestValidator(manifestOrFields, options = {}) {
21733
+ const isDraft = options.mode === "draft";
21734
+ const shape = {};
21735
+ const fields3 = Array.isArray(manifestOrFields) ? manifestOrFields : manifestOrFields.fields ?? [];
21736
+ for (const field of fields3) {
21737
+ shape[field.id] = buildFieldSchema(field, options);
21738
+ }
21739
+ const baseSchema = z.object(shape).catchall(z.unknown()).passthrough();
21740
+ if (isDraft) {
21741
+ return z.preprocess(preprocessEmptyValues, baseSchema);
21742
+ }
21743
+ return baseSchema;
21744
+ }
21745
+ function buildFieldSchema(field, options) {
21746
+ return fieldPlanToZod(deriveFieldValidationPlan(field), options);
21506
21747
  }
21507
21748
 
21508
21749
  // ../theme-core/src/site-styles/types.ts
@@ -23465,8 +23706,8 @@ z.discriminatedUnion("kind", [
23465
23706
  }).strict()
23466
23707
  ]);
23467
23708
  function validateBlockSequence(snapshots, ctx) {
23468
- for (const issue of collectBlockSequenceIssues(snapshots)) {
23469
- ctx.addIssue(issue);
23709
+ for (const issue2 of collectBlockSequenceIssues(snapshots)) {
23710
+ ctx.addIssue(issue2);
23470
23711
  }
23471
23712
  }
23472
23713
  function collectBlockSequenceIssues(snapshots) {
@@ -24511,7 +24752,7 @@ var sectionStateToneTransform = {
24511
24752
  summary: "Map semantic section-state kind to feedback tone",
24512
24753
  run: (value) => sectionStateToneFromUnknown(value)
24513
24754
  };
24514
- function isPlainObject(value) {
24755
+ function isPlainObject2(value) {
24515
24756
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
24516
24757
  }
24517
24758
  function readString(obj, key) {
@@ -24557,7 +24798,7 @@ var heroContentWrapperClassTransform = {
24557
24798
  summary: "Resolve hero content wrapper className from alignment + max width controls",
24558
24799
  schema: heroContentWrapperClassSchema,
24559
24800
  run: (value, options, context) => {
24560
- const content = isPlainObject(value) ? value : isPlainObject(context.data) ? context.data : {};
24801
+ const content = isPlainObject2(value) ? value : isPlainObject2(context.data) ? context.data : {};
24561
24802
  const alignment = resolveHeroContentAlignment(content);
24562
24803
  const maxWidth = resolveHeroContentMaxWidth(content);
24563
24804
  const textAlignClass = alignment === "center" ? "rb-text-center" : alignment === "right" ? "rb-text-right" : "rb-text-left";
@@ -24576,7 +24817,7 @@ var heroCtaJustifyTransform = {
24576
24817
  kind: "string",
24577
24818
  summary: "Map hero contentAlignment to CTA row justify value",
24578
24819
  run: (value, _options, context) => {
24579
- const content = isPlainObject(value) ? value : isPlainObject(context.data) ? context.data : {};
24820
+ const content = isPlainObject2(value) ? value : isPlainObject2(context.data) ? context.data : {};
24580
24821
  const alignment = resolveHeroContentAlignment(content);
24581
24822
  return alignment === "left" ? "start" : alignment === "right" ? "end" : "center";
24582
24823
  }
@@ -24586,7 +24827,7 @@ var heroStackJustifyFromVerticalAlignmentTransform = {
24586
24827
  kind: "string",
24587
24828
  summary: "Map hero verticalAlignment to stack justify value",
24588
24829
  run: (value, _options, context) => {
24589
- const content = isPlainObject(value) ? value : isPlainObject(context.data) ? context.data : {};
24830
+ const content = isPlainObject2(value) ? value : isPlainObject2(context.data) ? context.data : {};
24590
24831
  const vertical = resolveHeroVerticalAlignment(content);
24591
24832
  return vertical === "top" ? "start" : vertical === "bottom" ? "end" : "center";
24592
24833
  }
@@ -24600,7 +24841,7 @@ var heroGridClassFromVerticalAlignmentTransform = {
24600
24841
  summary: "Append align-items class for hero split layouts based on verticalAlignment",
24601
24842
  schema: heroGridClassFromVerticalAlignmentSchema,
24602
24843
  run: (value, options, context) => {
24603
- const content = isPlainObject(value) ? value : isPlainObject(context.data) ? context.data : {};
24844
+ const content = isPlainObject2(value) ? value : isPlainObject2(context.data) ? context.data : {};
24604
24845
  const vertical = resolveHeroVerticalAlignment(content);
24605
24846
  const itemsClass = vertical === "top" ? "rb-items-start" : vertical === "bottom" ? "rb-items-end" : "rb-items-center";
24606
24847
  return joinClasses3([options.base, itemsClass]);
@@ -32015,6 +32256,74 @@ init_shadow2();
32015
32256
  init_blocks();
32016
32257
  init_colorStyles();
32017
32258
 
32259
+ // ../api/src/navigation/linkValue.ts
32260
+ init_src();
32261
+ var Rfc6902PatchOp = z.discriminatedUnion("op", [
32262
+ // Standard RFC-6902 operations
32263
+ z.object({
32264
+ op: z.literal("add"),
32265
+ path: z.string(),
32266
+ value: z.unknown()
32267
+ }),
32268
+ z.object({
32269
+ op: z.literal("remove"),
32270
+ path: z.string()
32271
+ }),
32272
+ z.object({
32273
+ op: z.literal("replace"),
32274
+ path: z.string(),
32275
+ value: z.unknown()
32276
+ }),
32277
+ z.object({
32278
+ op: z.literal("move"),
32279
+ from: z.string(),
32280
+ path: z.string()
32281
+ }),
32282
+ z.object({
32283
+ op: z.literal("copy"),
32284
+ from: z.string(),
32285
+ path: z.string()
32286
+ }),
32287
+ // Block-level operations (Phase 2)
32288
+ z.object({
32289
+ op: z.literal("add_block"),
32290
+ blockKind: z.string(),
32291
+ afterBlockId: z.string().nullable(),
32292
+ content: z.record(z.string(), z.unknown()),
32293
+ rationale: z.string()
32294
+ }),
32295
+ z.object({
32296
+ op: z.literal("delete_block"),
32297
+ blockId: z.string(),
32298
+ rationale: z.string()
32299
+ }),
32300
+ z.object({
32301
+ op: z.literal("reorder_block"),
32302
+ blockId: z.string(),
32303
+ afterBlockId: z.string().nullable(),
32304
+ rationale: z.string()
32305
+ })
32306
+ ]);
32307
+ z.object({
32308
+ request: z.string().min(1).max(2e3)
32309
+ });
32310
+ var MultiPagePatchEnvelope = z.object({
32311
+ pageId: z.string(),
32312
+ blockId: z.string().optional(),
32313
+ // Not present for page-level ops
32314
+ blockKind: z.string().optional(),
32315
+ blockPurpose: z.string().optional().nullable(),
32316
+ ops: z.array(Rfc6902PatchOp),
32317
+ rationale: z.string(),
32318
+ currentContent: z.record(z.string(), z.unknown()).optional()
32319
+ });
32320
+ z.object({
32321
+ patches: z.array(MultiPagePatchEnvelope),
32322
+ assistantMessage: z.string(),
32323
+ pagesModified: z.number(),
32324
+ toolCallsUsed: z.number()
32325
+ });
32326
+
32018
32327
  // src/types/block-kinds.ts
32019
32328
  var SYSTEM_BLOCK_KINDS = [
32020
32329
  // Layout blocks
@@ -32242,7 +32551,7 @@ var fieldBindingSchema = z.object({
32242
32551
  var metaBindingSchema = z.object({
32243
32552
  source: z.literal("meta"),
32244
32553
  path: z.enum(TEMPLATE_BINDING_META_PATHS, {
32245
- error: (issue) => describeInvalidTemplateBindingMetaPath(String(issue.input))
32554
+ error: (issue2) => describeInvalidTemplateBindingMetaPath(String(issue2.input))
32246
32555
  })
32247
32556
  });
32248
32557
  var literalBindingSchema = z.object({
@@ -32253,7 +32562,7 @@ var transformBindingSchema = z.object({
32253
32562
  source: z.literal("transform"),
32254
32563
  path: z.string().min(1, "Transform path is required"),
32255
32564
  transform: z.enum(BINDING_TRANSFORM_VALUES, {
32256
- error: (issue) => describeInvalidBindingTransform(String(issue.input))
32565
+ error: (issue2) => describeInvalidBindingTransform(String(issue2.input))
32257
32566
  })
32258
32567
  });
32259
32568
  var baseBindingSchema = z.discriminatedUnion("source", [
@@ -34659,9 +34968,9 @@ async function pushConfigAction(output, options) {
34659
34968
  const parseResult = riverbankSiteConfigSchema.safeParse(rawConfig);
34660
34969
  if (!parseResult.success) {
34661
34970
  output.error("Invalid config", {
34662
- issues: parseResult.error.issues.map((issue) => ({
34663
- path: issue.path.join("."),
34664
- message: issue.message
34971
+ issues: parseResult.error.issues.map((issue2) => ({
34972
+ path: issue2.path.join("."),
34973
+ message: issue2.message
34665
34974
  }))
34666
34975
  });
34667
34976
  }
@@ -35595,7 +35904,7 @@ async function readSettings(contentDir) {
35595
35904
  if (!parsed.success) {
35596
35905
  throw new Error(
35597
35906
  `${filePath}: invalid settings.json
35598
- ` + parsed.error.issues.map((issue) => ` - ${issue.path.join(".") || "(root)"}: ${issue.message}`).join("\n")
35907
+ ` + parsed.error.issues.map((issue2) => ` - ${issue2.path.join(".") || "(root)"}: ${issue2.message}`).join("\n")
35599
35908
  );
35600
35909
  }
35601
35910
  return parsed.data;
@@ -38436,15 +38745,15 @@ function validateBlockContent(data, manifest, customFields) {
38436
38745
  validator.parse(data);
38437
38746
  } catch (error) {
38438
38747
  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") {
38748
+ for (const issue2 of error.issues) {
38749
+ const fieldPath = issue2.path.join(".");
38750
+ if (issue2.code === "invalid_type" && issue2.received === "undefined") {
38442
38751
  continue;
38443
38752
  }
38444
38753
  warnings.push({
38445
38754
  type: "invalid_content",
38446
38755
  field: fieldPath || "(root)",
38447
- message: `Invalid content${fieldPath ? ` in "${fieldPath}"` : ""}: ${issue.message}`
38756
+ message: `Invalid content${fieldPath ? ` in "${fieldPath}"` : ""}: ${issue2.message}`
38448
38757
  });
38449
38758
  }
38450
38759
  }
@@ -41309,7 +41618,7 @@ async function handleThemePushForAllScope(params) {
41309
41618
  }
41310
41619
  const validation = themeSchema2.safeParse(localTheme.theme);
41311
41620
  if (!validation.success) {
41312
- const errors = validation.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
41621
+ const errors = validation.error.issues.map((issue2) => ` - ${issue2.path.join(".")}: ${issue2.message}`).join("\n");
41313
41622
  output.warn(
41314
41623
  `Skipping theme push - invalid theme at content/themes/${themeName}.json:
41315
41624
  ${errors}`
@@ -42022,7 +42331,7 @@ ${available.map((t) => ` - ${t.name} (content/themes/${t.name}.json)`).join("\n
42022
42331
  }
42023
42332
  const validation = themeSchema.safeParse(localTheme.theme);
42024
42333
  if (!validation.success) {
42025
- const errors = validation.error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
42334
+ const errors = validation.error.issues.map((issue2) => ` - ${issue2.path.join(".")}: ${issue2.message}`).join("\n");
42026
42335
  output.error(
42027
42336
  `Invalid theme file at content/themes/${themeName}.json:
42028
42337
  ${errors}`
@@ -45110,7 +45419,7 @@ var SimpleCache = class {
45110
45419
  };
45111
45420
 
45112
45421
  // src/version.ts
45113
- var SDK_VERSION = "0.60.7";
45422
+ var SDK_VERSION = "0.60.11";
45114
45423
 
45115
45424
  // src/client/error.ts
45116
45425
  var RiverbankApiError = class _RiverbankApiError extends Error {
@@ -46913,9 +47222,9 @@ Examples:
46913
47222
  const parseResult = verifyConfigSchema.safeParse(rawConfig);
46914
47223
  if (!parseResult.success) {
46915
47224
  return output.error("Invalid config format", {
46916
- issues: parseResult.error.issues.map((issue) => ({
46917
- path: issue.path.join("."),
46918
- message: issue.message
47225
+ issues: parseResult.error.issues.map((issue2) => ({
47226
+ path: issue2.path.join("."),
47227
+ message: issue2.message
46919
47228
  })),
46920
47229
  suggestion: "Ensure your config exports a valid riverbank config object"
46921
47230
  });
@@ -47443,8 +47752,8 @@ async function executeAuditForEnv(output, target, options, isJsonOutput) {
47443
47752
  client
47444
47753
  });
47445
47754
  const mediaIssues = mapMediaGuardDiagnosticsToAuditIssues(mediaGuardResult.diagnostics);
47446
- errors.push(...mediaIssues.filter((issue) => issue.severity === "error"));
47447
- warnings.push(...mediaIssues.filter((issue) => issue.severity === "warning"));
47755
+ errors.push(...mediaIssues.filter((issue2) => issue2.severity === "error"));
47756
+ warnings.push(...mediaIssues.filter((issue2) => issue2.severity === "warning"));
47448
47757
  }
47449
47758
  if (!isJsonOutput) {
47450
47759
  output.info("Checking for duplicate page paths...");
@@ -47468,8 +47777,8 @@ async function executeAuditForEnv(output, target, options, isJsonOutput) {
47468
47777
  }
47469
47778
  errors.push(...auditEventReferences(localContent.events, auditCtx));
47470
47779
  const courseRefIssues = auditCourseReferences(localContent.courses, auditCtx);
47471
- errors.push(...courseRefIssues.filter((issue) => issue.severity === "error"));
47472
- warnings.push(...courseRefIssues.filter((issue) => issue.severity === "warning"));
47780
+ errors.push(...courseRefIssues.filter((issue2) => issue2.severity === "error"));
47781
+ warnings.push(...courseRefIssues.filter((issue2) => issue2.severity === "warning"));
47473
47782
  const success = errors.length === 0;
47474
47783
  const result = { success, errors, warnings };
47475
47784
  if (isJsonOutput) {
@@ -47484,14 +47793,14 @@ async function executeAuditForEnv(output, target, options, isJsonOutput) {
47484
47793
  output.info("");
47485
47794
  if (errors.length > 0) {
47486
47795
  output.warn(`Found ${errors.length} error(s):`);
47487
- for (const issue of errors) {
47488
- output.info(` [ERROR] ${issue.message}`);
47796
+ for (const issue2 of errors) {
47797
+ output.info(` [ERROR] ${issue2.message}`);
47489
47798
  }
47490
47799
  }
47491
47800
  if (warnings.length > 0) {
47492
47801
  output.warn(`Found ${warnings.length} warning(s):`);
47493
- for (const issue of warnings) {
47494
- output.info(` [WARN] ${issue.message}`);
47802
+ for (const issue2 of warnings) {
47803
+ output.info(` [WARN] ${issue2.message}`);
47495
47804
  }
47496
47805
  }
47497
47806
  if (success && warnings.length === 0) {
@@ -51523,7 +51832,7 @@ Examples:
51523
51832
  $ riverbankcms course delete beginner-yoga --yes
51524
51833
  `
51525
51834
  ).addCommand(listCommand7).addCommand(getCommand6).addCommand(upsertCommand6).addCommand(cancelCommand2).addCommand(deleteCommand3);
51526
- function isPlainObject2(value) {
51835
+ function isPlainObject3(value) {
51527
51836
  return !!value && typeof value === "object" && !Array.isArray(value);
51528
51837
  }
51529
51838
  function migrateLegacyEntryTitleInPlace(entry) {
@@ -51531,7 +51840,7 @@ function migrateLegacyEntryTitleInPlace(entry) {
51531
51840
  const titleValue = entry.title;
51532
51841
  delete entry.title;
51533
51842
  let migratedToDataTitle = false;
51534
- if (typeof titleValue === "string" && isPlainObject2(entry.data)) {
51843
+ if (typeof titleValue === "string" && isPlainObject3(entry.data)) {
51535
51844
  const data = entry.data;
51536
51845
  if (!("title" in data)) {
51537
51846
  data.title = titleValue;
@@ -51557,7 +51866,7 @@ async function backupLegacyFile(contentDir, legacyFilePath, dryRun) {
51557
51866
  }
51558
51867
  async function migrateLegacyEntriesFile(contentDir, legacyFilePath, options) {
51559
51868
  const raw = await readJsonFile(legacyFilePath);
51560
- if (!isPlainObject2(raw)) {
51869
+ if (!isPlainObject3(raw)) {
51561
51870
  throw new Error(`Legacy entries file is not an object: ${legacyFilePath}`);
51562
51871
  }
51563
51872
  const contentType = raw.contentType;
@@ -51578,14 +51887,14 @@ async function migrateLegacyEntriesFile(contentDir, legacyFilePath, options) {
51578
51887
  let legacyTitlesMigrated = 0;
51579
51888
  let legacyTitleFilesUpdated = 0;
51580
51889
  for (const entryUnknown of entries) {
51581
- if (!isPlainObject2(entryUnknown)) {
51890
+ if (!isPlainObject3(entryUnknown)) {
51582
51891
  throw new Error(`Invalid legacy entry (expected object): ${legacyFilePath}`);
51583
51892
  }
51584
51893
  const identifier = entryUnknown.identifier;
51585
51894
  if (typeof identifier !== "string" || identifier.length === 0) {
51586
51895
  throw new Error(`Invalid legacy entry identifier in: ${legacyFilePath}`);
51587
51896
  }
51588
- if (!isPlainObject2(entryUnknown.data)) {
51897
+ if (!isPlainObject3(entryUnknown.data)) {
51589
51898
  throw new Error(`Invalid legacy entry data (expected object) for "${identifier}" in: ${legacyFilePath}`);
51590
51899
  }
51591
51900
  const { updated, migratedToDataTitle } = migrateLegacyEntryTitleInPlace(entryUnknown);
@@ -51620,7 +51929,7 @@ async function migrateLegacyTitlesInDirectory(typeDir, options) {
51620
51929
  for (const filePath of files) {
51621
51930
  scanned += 1;
51622
51931
  const raw = await readJsonFile(filePath);
51623
- if (!isPlainObject2(raw)) continue;
51932
+ if (!isPlainObject3(raw)) continue;
51624
51933
  const { updated, migratedToDataTitle } = migrateLegacyEntryTitleInPlace(raw);
51625
51934
  if (!updated) continue;
51626
51935
  updatedFiles += 1;