@relyio/cli 0.4.0 → 0.6.0

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 (2) hide show
  1. package/dist/index.js +382 -1271
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -920,10 +920,10 @@ var init_exit_codes = __esm({
920
920
  }
921
921
  });
922
922
 
923
- // ../../platform/api-contract/dist/chunk-HN42AOXD.js
923
+ // ../../platform/api-contract/dist/chunk-PVEJCUTN.js
924
924
  var ERROR_CODES;
925
- var init_chunk_HN42AOXD = __esm({
926
- "../../platform/api-contract/dist/chunk-HN42AOXD.js"() {
925
+ var init_chunk_PVEJCUTN = __esm({
926
+ "../../platform/api-contract/dist/chunk-PVEJCUTN.js"() {
927
927
  ERROR_CODES = {
928
928
  MISSING_API_KEY: { status: 401 },
929
929
  INVALID_API_KEY: { status: 401 },
@@ -1002,6 +1002,25 @@ var init_chunk_HN42AOXD = __esm({
1002
1002
  * 2026-09-27 (REL-1316).
1003
1003
  */
1004
1004
  ORGANIZATION_DELETE_BLOCKED: { status: 409 },
1005
+ /**
1006
+ * Added by REL-1730, the fifth name added to §4's approved seventeen.
1007
+ *
1008
+ * A Clerk session is valid the moment someone signs up, and the User's row
1009
+ * in Manage is written later, when Clerk's `user.created` webhook arrives.
1010
+ * In between, Manage refused every request with `INVALID_API_KEY` — the code
1011
+ * a forged token gets — so only the message told "wait" from "sign in
1012
+ * again", and a new invitee landing after sign-up met exactly that window.
1013
+ * It passes Decision 3's test: a client reading it keeps the session and
1014
+ * asks again after `Retry-After`, which no other code tells it. 503 with
1015
+ * `Retry-After` because it is a wait on a dependency rather than a refused
1016
+ * credential (`INV-API-INDETERMINATE-HAS-A-STATUS`); `SERVICE_INDETERMINATE`
1017
+ * was not reused because a client cannot tell from it that the credential
1018
+ * is good and the wait is bounded by a webhook.
1019
+ *
1020
+ * A **customer-facing naming decision**, approved by the product owner on
1021
+ * 2026-10-01 (REL-1721).
1022
+ */
1023
+ USER_NOT_PROVISIONED: { status: 503 },
1005
1024
  RESOURCE_NOT_FOUND: { status: 404 },
1006
1025
  RESOURCE_CONFLICT: { status: 409 },
1007
1026
  RESOURCE_EXPIRED: { status: 410 },
@@ -1048,7 +1067,7 @@ function readError(body) {
1048
1067
  var ContractBodyError;
1049
1068
  var init_client2 = __esm({
1050
1069
  "../../platform/api-contract/dist/client/index.js"() {
1051
- init_chunk_HN42AOXD();
1070
+ init_chunk_PVEJCUTN();
1052
1071
  ContractBodyError = class extends Error {
1053
1072
  body;
1054
1073
  constructor(message, body) {
@@ -1486,8 +1505,8 @@ function isBuiltInRole(roleId) {
1486
1505
  return BUILT_IN_ROLES.includes(roleId);
1487
1506
  }
1488
1507
  var FallbackChainSchema, BaseFieldConfigSchema, ShortTextConfigSchema, LongTextConfigSchema, RichTextConfigSchema, EmailConfigSchema, UrlConfigSchema, SlugConfigSchema, IntegerConfigSchema, DecimalConfigSchema, BooleanConfigSchema, EnumConfigSchema, DateConfigSchema, DateTimeConfigSchema, ReferenceStrengthSchema, OnTargetDeleteSchema, ReferenceTargetSchema, ReferenceConfigSchema, AI_INSTRUCTIONS_MAX_CHARS, AIConfigSchema, FieldAIConfigSchema, AssetAIConfigSchema, BaseUIConfigSchema, TextUIOptionsSchema, LongTextUIOptionsSchema, RichTextUIOptionsSchema, SlugUIOptionsSchema, NumericUIOptionsSchema, BooleanUIOptionsSchema, EnumUIOptionsSchema, DateUIOptionsSchema, DateTimeUIOptionsSchema, ReferenceUIOptionsSchema, TagsUIOptionsSchema, ListUIOptionsSchema, ComponentUIOptionsSchema, UIConfigSchema, RuleConditionSchema, RuleConditionsSchema, VisibilityRuleSchema, RequirementRuleSchema, ValidationRuleSchema, ReadOnlyRuleSchema, FieldRuleSchema, LocalizationConfigSchema, FieldSchema, TagsConfigSchema, ListConfigSchema, BUILT_IN_ROLES, ROLE_ACTIONS, RoleActionSchema, BuiltInRoleSchema, roleIdPattern, RoleIdSchema, accessTagPattern, AccessTagSchema, AccessTagsArraySchema, ResourcePermissionSchema, FieldVisibilitySchema, RolePermissionsSchema, RoleSchema, RolesArraySchema, FILTER_OPERATORS, FilterOperatorSchema, TEXT_OPERATORS, LONG_TEXT_OPERATORS, NUMERIC_OPERATORS, BOOLEAN_OPERATORS, ENUM_OPERATORS, DATE_OPERATORS, REFERENCE_OPERATORS, TAGS_OPERATORS, FIELD_TYPE_OPERATORS, TimeUnitSchema, RelativeDateValueSchema, COLUMN_RENDERERS, ColumnRendererSchema;
1489
- var init_chunk_BUTRMHUP = __esm({
1490
- "../../domain/schema/dist/chunk-BUTRMHUP.js"() {
1508
+ var init_chunk_MTW7TUIF = __esm({
1509
+ "../../domain/schema/dist/chunk-MTW7TUIF.js"() {
1491
1510
  init_chunk_6PLZZWGE();
1492
1511
  init_chunk_JH6U6ENN();
1493
1512
  FallbackChainSchema = z.object({
@@ -2508,9 +2527,9 @@ function resolveAssetType(def) {
2508
2527
  };
2509
2528
  }
2510
2529
  var FieldsetSchema, stageIdPattern, WorkflowEscalationSchema, WorkflowAutoAssignSchema, WorkflowStageSchema, WorkflowConfigSchema, ContentTypeWorkflowSchema, ASSET_KINDS, FORBIDDEN_ASSET_TYPE_FIELD_TYPES, IMAGE_OUTPUT_FORMATS, IMAGE_FIT_MODES, CF_IMAGES_LIMITS, DEFAULT_IMAGE_SOURCE, UNKNOWN_INPUT_MESSAGE, INPUT_CYCLE_MESSAGE, FitSchema, OutputFormatSchema, ImageTransformSchema, ImageUploadConfigSchema, VideoUploadConfigSchema, FileUploadConfigSchema, ImageExifConfigSchema, VariantDefinitionSchema, ImageSourceConfigSchema, ImageKindConfigSchema, VideoPosterConfigSchema, VideoCaptionsConfigSchema, VideoDownloadConfigSchema, VideoWatermarkConfigSchema, VideoKindConfigSchema, FileScanConfigSchema, FileKindConfigSchema, AssetTypeSchema;
2511
- var init_chunk_CE6CNF7I = __esm({
2512
- "../../domain/schema/dist/chunk-CE6CNF7I.js"() {
2513
- init_chunk_BUTRMHUP();
2530
+ var init_chunk_SLHDFP7X = __esm({
2531
+ "../../domain/schema/dist/chunk-SLHDFP7X.js"() {
2532
+ init_chunk_MTW7TUIF();
2514
2533
  FieldsetSchema = z.object({
2515
2534
  id: z.string().regex(
2516
2535
  /^[a-zA-Z][a-zA-Z0-9_]*$/,
@@ -3043,6 +3062,17 @@ function parseSelectExpression(expression) {
3043
3062
  }
3044
3063
  return { ok: true, path: path25, operation };
3045
3064
  }
3065
+ function isFilterGroupShape(value) {
3066
+ return value !== null && typeof value === "object" && !Array.isArray(value) && Object.hasOwn(value, "conditions");
3067
+ }
3068
+ function refusingFilterGroups(schema, message) {
3069
+ return z.preprocess((value, ctx) => {
3070
+ if (isFilterGroupShape(value)) {
3071
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message, fatal: true });
3072
+ }
3073
+ return value;
3074
+ }, schema);
3075
+ }
3046
3076
  function filterValueWith(range, relativeDate) {
3047
3077
  return z.union([
3048
3078
  // Simple values
@@ -3062,11 +3092,11 @@ function filterValueWith(range, relativeDate) {
3062
3092
  // ISO date string
3063
3093
  ]);
3064
3094
  }
3065
- var QUERY_BLOCKS, queryBlocksOf, QUERY_BLOCKS_SPELLED, queryRunsBlock, QUERY_FILTER_OPERATORS, isQueryFilterOperator, QUERY_FILTER_OPERATOR_NAMES, SPELLED, FIELD_PATH, AGGREGATE_OPERATORS, isAggregateOperator, AGGREGATE_OPERATOR_NAMES, INCLUDE_MODIFIER_NAMES, INCLUDE_MODIFIERS, isIncludeModifier, INCLUDE_EXPRESSION, SUPPORTED, MAX_SELECT_OPERATION_ARGUMENT, withoutArguments, SELECT_STRING_OPERATIONS, isSelectStringOperationName, SELECT_STRING_OPERATION_NAMES, OPERATION_SPELLING, NON_NEGATIVE_INTEGER, VIEW_SLUG_PATTERN, VIEW_SLUG_MESSAGE, RangeValueSchema, FilterValueSchema, FilterConditionSchema, LogicalOperatorSchema, FilterGroupSchema, ColumnConfigSchema, MetadataFieldConfigSchema, MetadataLineConfigSchema, TableSettingsSchema, ViewScopeSchema, contentTypeScope, contentTypesScope, allScope, entryTypeScope, DeclaredViewScopeSchema, DeclaredFilterValueSchema, ViewDSLSchema, ViewVisibilitySchema, ViewSourceSchema, SchemaViewDefinitionSchema;
3066
- var init_chunk_QG7U6KYM = __esm({
3067
- "../../domain/schema/dist/chunk-QG7U6KYM.js"() {
3095
+ var QUERY_BLOCKS, queryBlocksOf, QUERY_BLOCKS_SPELLED, queryRunsBlock, QUERY_FILTER_OPERATORS, isQueryFilterOperator, QUERY_FILTER_OPERATOR_NAMES, SPELLED, FIELD_PATH, AGGREGATE_OPERATORS, isAggregateOperator, AGGREGATE_OPERATOR_NAMES, INCLUDE_MODIFIER_NAMES, INCLUDE_MODIFIERS, isIncludeModifier, INCLUDE_EXPRESSION, SUPPORTED, MAX_SELECT_OPERATION_ARGUMENT, withoutArguments, SELECT_STRING_OPERATIONS, isSelectStringOperationName, SELECT_STRING_OPERATION_NAMES, OPERATION_SPELLING, NON_NEGATIVE_INTEGER, VIEW_SLUG_PATTERN, VIEW_SLUG_MESSAGE, SCHEMA_VIEW_FILTER_GROUP_REFUSAL, SAVED_VIEW_FILTER_GROUP_REFUSAL, RangeValueSchema, FilterValueSchema, FilterConditionSchema, LogicalOperatorSchema, FilterGroupSchema, ColumnConfigSchema, MetadataFieldConfigSchema, MetadataLineConfigSchema, TableSettingsSchema, ViewScopeSchema, contentTypeScope, contentTypesScope, allScope, entryTypeScope, DeclaredViewScopeSchema, DeclaredFilterValueSchema, SchemaViewFilterSchema, ViewDSLSchema, ViewVisibilitySchema, ViewSourceSchema, SchemaViewDefinitionSchema;
3096
+ var init_chunk_5WZPX5RR = __esm({
3097
+ "../../domain/schema/dist/chunk-5WZPX5RR.js"() {
3068
3098
  init_chunk_46JQ2NQ5();
3069
- init_chunk_BUTRMHUP();
3099
+ init_chunk_MTW7TUIF();
3070
3100
  QUERY_BLOCKS = ["fetch", "aggregate", "search"];
3071
3101
  queryBlocksOf = (query) => QUERY_BLOCKS.filter((block) => Boolean(query[block]));
3072
3102
  QUERY_BLOCKS_SPELLED = QUERY_BLOCKS.join(", ");
@@ -3166,6 +3196,8 @@ var init_chunk_QG7U6KYM = __esm({
3166
3196
  NON_NEGATIVE_INTEGER = /^\d+$/;
3167
3197
  VIEW_SLUG_PATTERN = /^[a-z][a-z0-9-]*$/;
3168
3198
  VIEW_SLUG_MESSAGE = "Slug must be lowercase, start with a letter, and contain only letters, numbers, and hyphens";
3199
+ SCHEMA_VIEW_FILTER_GROUP_REFUSAL = "A view's filters are a flat list, joined by AND: a filter group (an `operator` with `conditions`) is not accepted. List each of its conditions as a filter of its own, or use `isAnyOf` for an OR within one field.";
3200
+ SAVED_VIEW_FILTER_GROUP_REFUSAL = "A View's filters are flat Conditions, joined by AND: a filter group (an `operator` with `conditions`) is not accepted. Save each of its conditions as a Condition of its own, or use `in` for an OR within one Property.";
3169
3201
  RangeValueSchema = z.object({
3170
3202
  from: z.union([z.string(), z.number(), z.date()]),
3171
3203
  to: z.union([z.string(), z.number(), z.date()])
@@ -3356,6 +3388,14 @@ var init_chunk_QG7U6KYM = __esm({
3356
3388
  RangeValueSchema.strict(),
3357
3389
  RelativeDateValueSchema.strict()
3358
3390
  );
3391
+ SchemaViewFilterSchema = refusingFilterGroups(
3392
+ z.object({
3393
+ field: z.string(),
3394
+ operator: FilterOperatorSchema,
3395
+ value: DeclaredFilterValueSchema.optional()
3396
+ }).strict(),
3397
+ SCHEMA_VIEW_FILTER_GROUP_REFUSAL
3398
+ );
3359
3399
  ViewDSLSchema = z.object({
3360
3400
  /**
3361
3401
  * View scope - what content types this view applies to
@@ -3487,29 +3527,10 @@ var init_chunk_QG7U6KYM = __esm({
3487
3527
  */
3488
3528
  scope: DeclaredViewScopeSchema,
3489
3529
  /**
3490
- * Filter configuration (simplified for YAML)
3530
+ * The view's filters: a flat list, joined by AND. A filter group is refused by name
3531
+ * (REL-1630).
3491
3532
  */
3492
- filters: z.array(
3493
- z.union([
3494
- // Simple condition
3495
- z.object({
3496
- field: z.string(),
3497
- operator: FilterOperatorSchema,
3498
- value: DeclaredFilterValueSchema.optional()
3499
- }).strict(),
3500
- // Group
3501
- z.object({
3502
- operator: LogicalOperatorSchema,
3503
- conditions: z.array(
3504
- z.object({
3505
- field: z.string(),
3506
- operator: FilterOperatorSchema,
3507
- value: DeclaredFilterValueSchema.optional()
3508
- }).strict()
3509
- )
3510
- }).strict()
3511
- ])
3512
- ).optional(),
3533
+ filters: z.array(SchemaViewFilterSchema).optional(),
3513
3534
  /**
3514
3535
  * Columns to display (field IDs)
3515
3536
  */
@@ -3686,13 +3707,13 @@ function isCommonProtocol(protocol) {
3686
3707
  const commonProtocols = ["http", "https", "ftp", "ftps", "sftp", "file", "mailto", "tel"];
3687
3708
  return commonProtocols.includes(protocol.toLowerCase());
3688
3709
  }
3689
- var QUERY_ENDPOINT_SEGMENT, QUERY_ENDPOINT_PATTERN, PARAMETER_EXPRESSION_SOURCE, parameterExpressionMatcher, ANCHORED_PARAMETER_EXPRESSION, parseDefault, parseParameterExpression, collectParameterExpressions, endpointPathParameters, ParameterExpressionStringSchema, QUERY_PARAMETER_TYPES, QueryParameterTypeSchema, QueryParameterScalarSchema, QueryParameterSchema, typeOfDefault, declareQueryParameters, SEARCH_SCORE_SORT_KEYS, isSearchScoreSortKey, QUERY_SYSTEM_FIELDS, isQuerySystemField, QUERY_STORAGE_PATH_PREFIX, QUERY_SYSTEM_ROOTS, SEARCH_SELECT_COLUMNS, isSearchSelectColumn, parseDynamicParameter, ParameterizedLimitSchema, ParameterizedOffsetSchema, ScalarSchema, WhereOperatorObjectSchema, FilterOperatorObjectSchema, HavingOperatorObjectSchema, WhereValueSchema, WhereConditionSchema, SortConditionSchema, EnhancedIncludeObjectSchema, IncludeDefinitionSchema, SelectMappingSchema, FilterDefinitionSchema, FullTextSearchSchema, ContentTypesSchema, SearchBlockSchema, AggregationMetricSchema, QueryCardinalitySchema, QueryCachePolicySchema, FetchBlockSchema, AggregateBlockSchema, IncludeSubQueriesSchema, QueryDefinitionSchema, CACHE_POLICY_STRING, normalizeQueryCachePolicy, CONTENT_API_ROUTE_SEGMENTS, RESERVED_TYPE_IDS, getComponentIdFromType, ComponentSchema, ContentTypeSchema, SettingsGroupSchema, SchemaDefinitionSchema;
3690
- var init_chunk_L6HCY63G = __esm({
3691
- "../../domain/schema/dist/chunk-L6HCY63G.js"() {
3692
- init_chunk_CE6CNF7I();
3693
- init_chunk_QG7U6KYM();
3710
+ var QUERY_ENDPOINT_SEGMENT, QUERY_ENDPOINT_PATTERN, PARAMETER_EXPRESSION_SOURCE, parameterExpressionMatcher, ANCHORED_PARAMETER_EXPRESSION, parseDefault, parseParameterExpression, collectParameterExpressions, endpointPathParameters, ParameterExpressionStringSchema, QUERY_PARAMETER_TYPES, QueryParameterTypeSchema, QueryParameterScalarSchema, QueryParameterSchema, typeOfDefault, declareQueryParameters, SEARCH_SCORE_SORT_KEYS, isSearchScoreSortKey, QUERY_SYSTEM_FIELDS, isQuerySystemField, QUERY_STORAGE_PATH_PREFIX, QUERY_SYSTEM_ROOTS, SEARCH_SELECT_COLUMNS, isSearchSelectColumn, parseDynamicParameter, ParameterizedLimitSchema, ParameterizedOffsetSchema, ScalarSchema, WhereOperatorObjectSchema, FilterOperatorObjectSchema, HavingOperatorObjectSchema, WhereValueSchema, WhereConditionSchema, SortConditionSchema, EnhancedIncludeObjectSchema, IncludeDefinitionSchema, SelectMappingSchema, FilterDefinitionSchema, FullTextSearchSchema, ContentTypesSchema, SearchBlockSchema, AggregationMetricSchema, QueryCardinalitySchema, QueryCachePolicySchema, FetchBlockSchema, AggregateBlockSchema, IncludeSubQueriesSchema, QueryDefinitionSchema, CACHE_POLICY_STRING, normalizeQueryCachePolicy, CONTENT_API_ROUTE_SEGMENTS, RESERVED_TYPE_IDS, getComponentIdFromType, ComponentSchema, ContentTypeSchema, SettingsGroupSchema, SchemaDefinitionSchema, SavedViewFilterSchema;
3711
+ var init_chunk_GHHX2DKQ = __esm({
3712
+ "../../domain/schema/dist/chunk-GHHX2DKQ.js"() {
3713
+ init_chunk_SLHDFP7X();
3714
+ init_chunk_5WZPX5RR();
3694
3715
  init_chunk_W6RFZ2TV();
3695
- init_chunk_BUTRMHUP();
3716
+ init_chunk_MTW7TUIF();
3696
3717
  QUERY_ENDPOINT_SEGMENT = "api";
3697
3718
  QUERY_ENDPOINT_PATTERN = new RegExp(
3698
3719
  String.raw`^\/${QUERY_ENDPOINT_SEGMENT}\/([a-zA-Z0-9\-_/]|\{[a-zA-Z][a-zA-Z0-9_]*\})*$`
@@ -4576,6 +4597,16 @@ var init_chunk_L6HCY63G = __esm({
4576
4597
  validateContentTypeWorkflow(assetTypeId, assetType, "assetTypes");
4577
4598
  }
4578
4599
  });
4600
+ SavedViewFilterSchema = refusingFilterGroups(
4601
+ z.record(z.string(), z.unknown()),
4602
+ SAVED_VIEW_FILTER_GROUP_REFUSAL
4603
+ );
4604
+ ViewDSLSchema.omit({ filters: true }).extend({
4605
+ filters: refusingFilterGroups(
4606
+ z.array(SavedViewFilterSchema),
4607
+ SAVED_VIEW_FILTER_GROUP_REFUSAL
4608
+ ).optional()
4609
+ });
4579
4610
  }
4580
4611
  });
4581
4612
  function emptyDefinition() {
@@ -4654,33 +4685,9 @@ var init_chunk_EGQAMYAG = __esm({
4654
4685
  }
4655
4686
  });
4656
4687
 
4657
- // ../../domain/schema/dist/chunk-LDBRHF5K.js
4658
- var TABLE_SYSTEM_FIELD_KINDS;
4659
- var init_chunk_LDBRHF5K = __esm({
4660
- "../../domain/schema/dist/chunk-LDBRHF5K.js"() {
4661
- init_chunk_BUTRMHUP();
4662
- init_chunk_6PLZZWGE();
4663
- TABLE_SYSTEM_FIELD_KINDS = {
4664
- id: "text",
4665
- contentType: "choice",
4666
- status: "text",
4667
- createdAt: "date",
4668
- updatedAt: "date",
4669
- publishedAt: "date",
4670
- createdBy: "reference",
4671
- updatedBy: "reference"
4672
- };
4673
- ({
4674
- ...TABLE_SYSTEM_FIELD_KINDS,
4675
- "file.status": "text",
4676
- "file.backend": "text",
4677
- "file.mimeType": "text",
4678
- "file.fileName": "text",
4679
- "file.size": "number",
4680
- "file.extracted.width": "number",
4681
- "file.extracted.height": "number",
4682
- "file.extracted.duration": "number"
4683
- });
4688
+ // ../../domain/schema/dist/chunk-IYJ7UCPY.js
4689
+ var init_chunk_IYJ7UCPY = __esm({
4690
+ "../../domain/schema/dist/chunk-IYJ7UCPY.js"() {
4684
4691
  }
4685
4692
  });
4686
4693
 
@@ -4701,15 +4708,56 @@ var init_chunk_QSWR2YJN = __esm({
4701
4708
  }
4702
4709
  });
4703
4710
 
4704
- // ../../domain/schema/dist/chunk-GICJZDXP.js
4705
- var init_chunk_GICJZDXP = __esm({
4706
- "../../domain/schema/dist/chunk-GICJZDXP.js"() {
4707
- init_chunk_CE6CNF7I();
4711
+ // ../../domain/schema/dist/chunk-VIL5NXDT.js
4712
+ var init_chunk_VIL5NXDT = __esm({
4713
+ "../../domain/schema/dist/chunk-VIL5NXDT.js"() {
4714
+ init_chunk_SLHDFP7X();
4708
4715
  init_chunk_QSWR2YJN();
4709
- init_chunk_BUTRMHUP();
4716
+ init_chunk_MTW7TUIF();
4710
4717
  init_chunk_6PLZZWGE();
4711
4718
  }
4712
4719
  });
4720
+
4721
+ // ../../domain/schema/dist/chunk-FTFPIW7B.js
4722
+ var TABLE_SYSTEM_FIELD_KINDS;
4723
+ var init_chunk_FTFPIW7B = __esm({
4724
+ "../../domain/schema/dist/chunk-FTFPIW7B.js"() {
4725
+ init_chunk_IYJ7UCPY();
4726
+ init_chunk_VIL5NXDT();
4727
+ init_chunk_MTW7TUIF();
4728
+ init_chunk_6PLZZWGE();
4729
+ TABLE_SYSTEM_FIELD_KINDS = {
4730
+ id: "text",
4731
+ contentType: "choice",
4732
+ status: "text",
4733
+ createdAt: "date",
4734
+ updatedAt: "date",
4735
+ publishedAt: "date",
4736
+ createdBy: "reference",
4737
+ updatedBy: "reference",
4738
+ entryType: "choice",
4739
+ localeStatus: "choice",
4740
+ hasUnpublishedWork: "boolean",
4741
+ releaseId: "reference",
4742
+ workflowStageId: "choice",
4743
+ workflowPaused: "boolean",
4744
+ workflowPausedReason: "choice",
4745
+ "validation.isValid": "boolean",
4746
+ publishedBy: "reference"
4747
+ };
4748
+ ({
4749
+ ...TABLE_SYSTEM_FIELD_KINDS,
4750
+ "file.status": "text",
4751
+ "file.backend": "text",
4752
+ "file.mimeType": "text",
4753
+ "file.fileName": "text",
4754
+ "file.size": "number",
4755
+ "file.extracted.width": "number",
4756
+ "file.extracted.height": "number",
4757
+ "file.extracted.duration": "number"
4758
+ });
4759
+ }
4760
+ });
4713
4761
  function containerIndex(record) {
4714
4762
  const entries = Object.entries(record ?? {});
4715
4763
  return {
@@ -5187,6 +5235,9 @@ function classifyCompatibility(diff2) {
5187
5235
  }
5188
5236
  return { level, safeToMerge, additionsOnly, breakingChanges };
5189
5237
  }
5238
+ function isCrossVersionCheckCode(code) {
5239
+ return Object.hasOwn(CROSS_VERSION_CHECKS, code);
5240
+ }
5190
5241
  function isConditionObject(value) {
5191
5242
  return typeof value === "object" && value !== null && !Array.isArray(value);
5192
5243
  }
@@ -5339,19 +5390,20 @@ function notComparedWarning(part, refusals) {
5339
5390
  suggestion: "Nothing to change for it: this deploy replaces the deployed definition, and the next deploy is compared against all of it"
5340
5391
  };
5341
5392
  }
5342
- var USAGE_CONTAINERS, BaseValidator, ValidationUtils, NamingValidator, StructuralValidator, FieldConfigValidator, CrossReferenceValidator, ComponentValidator, ComponentFieldTypeValidator, ComponentValidationUtils, ComponentReusabilityValidator, ComponentFieldStructureValidator, ComponentUsageContextValidator, ComponentNestingValidator, FieldsetValidator, CONTENT_TYPE_PROPS, COMPONENT_PROPS, GROUP_PROPS, QUERY_BASIC_PROPS, QUERY_OPTIONAL_PROPS, QUERY_DSL_BLOCKS, LOCALE_PROPS, ROLE_PROPS, WORKFLOW_STAGE_PROPS, VIEW_PROPS, FIELDSET_PROPS, ASSET_TYPE_PROPS, ASSET_KIND_BLOCKS, ASSET_KIND_KEY_MARKER, FILE_LOCALIZATION_MARKER, FIELD_REQUIRED_CHANGE_KEY, SchemaDiffer, ADDED_LABEL, CROSS_VERSION_CHECKS, EvolutionValidator, HierarchicalContentValidator, FieldRulesValidator, LocalizationValidator, LocaleConfigValidator, SettingsGroupsValidator, SingletonContentTypesValidator, FORBIDDEN_TYPES, AssetTypesValidator, FieldUIValidator, BENEATH, ANY_JOIN, DslQueryValidator, DataIntegrityValidator, RolesValidator, EVOLUTION_VALIDATOR_KEY, CROSS_VERSION_CHECKS_SKIPPED, ValidationPipeline, SchemaValidator, FIELD_SECTIONS, OPERATION_TIER, DEPLOY_VALIDATION_CONFIG;
5393
+ var USAGE_CONTAINERS, BaseValidator, ValidationUtils, NamingValidator, StructuralValidator, FieldConfigValidator, CrossReferenceValidator, ComponentValidator, ComponentFieldTypeValidator, ComponentValidationUtils, ComponentReusabilityValidator, ComponentFieldStructureValidator, ComponentUsageContextValidator, ComponentNestingValidator, FieldsetValidator, CONTENT_TYPE_PROPS, COMPONENT_PROPS, GROUP_PROPS, QUERY_BASIC_PROPS, QUERY_OPTIONAL_PROPS, QUERY_DSL_BLOCKS, LOCALE_PROPS, ROLE_PROPS, WORKFLOW_STAGE_PROPS, VIEW_PROPS, FIELDSET_PROPS, ASSET_TYPE_PROPS, ASSET_KIND_BLOCKS, ASSET_KIND_KEY_MARKER, FILE_LOCALIZATION_MARKER, FIELD_REQUIRED_CHANGE_KEY, SchemaDiffer, ADDED_LABEL, CROSS_VERSION_CHECKS, CROSS_VERSION_CODES, EvolutionValidator, HierarchicalContentValidator, FieldRulesValidator, LocalizationValidator, LocaleConfigValidator, SettingsGroupsValidator, SingletonContentTypesValidator, FORBIDDEN_TYPES, AssetTypesValidator, FieldUIValidator, BENEATH, ANY_JOIN, DslQueryValidator, DataIntegrityValidator, RolesValidator, EVOLUTION_VALIDATOR_KEY, CROSS_VERSION_CHECKS_SKIPPED, ValidationPipeline, SchemaValidator, FIELD_SECTIONS, OPERATION_TIER, DEPLOY_VALIDATION_CONFIG;
5343
5394
  var init_dist2 = __esm({
5344
5395
  "../../domain/schema/dist/index.js"() {
5345
- init_chunk_L6HCY63G();
5396
+ init_chunk_GHHX2DKQ();
5346
5397
  init_chunk_EGQAMYAG();
5347
- init_chunk_LDBRHF5K();
5348
- init_chunk_GICJZDXP();
5349
- init_chunk_CE6CNF7I();
5398
+ init_chunk_FTFPIW7B();
5399
+ init_chunk_IYJ7UCPY();
5400
+ init_chunk_VIL5NXDT();
5401
+ init_chunk_SLHDFP7X();
5350
5402
  init_chunk_QSWR2YJN();
5351
- init_chunk_QG7U6KYM();
5403
+ init_chunk_5WZPX5RR();
5352
5404
  init_chunk_46JQ2NQ5();
5353
5405
  init_chunk_W6RFZ2TV();
5354
- init_chunk_BUTRMHUP();
5406
+ init_chunk_MTW7TUIF();
5355
5407
  init_chunk_6PLZZWGE();
5356
5408
  init_chunk_JH6U6ENN();
5357
5409
  USAGE_CONTAINERS = [
@@ -5651,32 +5703,7 @@ var init_dist2 = __esm({
5651
5703
  this.checkNamingConventions(name, type, path25, result);
5652
5704
  }
5653
5705
  validateFieldName(field, path25, result, reservedKeywords) {
5654
- if (!field.id) {
5655
- result.errors.push(
5656
- this.createError(
5657
- "MISSING_FIELD_ID",
5658
- "Field is missing required id property",
5659
- path25,
5660
- "error",
5661
- "Add an id property to the field",
5662
- true
5663
- )
5664
- );
5665
- return;
5666
- }
5667
5706
  const fieldId = field.id;
5668
- if (!ValidationUtils.isValidName(fieldId)) {
5669
- result.errors.push(
5670
- this.createError(
5671
- "INVALID_FIELD_NAME",
5672
- `Field id '${fieldId}' is invalid. Must start with a letter and contain only letters, numbers, and underscores`,
5673
- `${path25}.id`,
5674
- "error",
5675
- 'Use a valid identifier format (e.g., "myField", "my_field")',
5676
- true
5677
- )
5678
- );
5679
- }
5680
5707
  if (reservedKeywords.includes(fieldId.toLowerCase())) {
5681
5708
  const suggestion = ValidationUtils.findBestMatch(fieldId, [
5682
5709
  "my" + fieldId,
@@ -5760,31 +5787,13 @@ var init_dist2 = __esm({
5760
5787
  feature = "structural";
5761
5788
  async validate(index) {
5762
5789
  const result = this.createResult();
5763
- try {
5764
- SchemaDefinitionSchema.parse(index.schema);
5765
- this.validateContentTypeStructure(index, result);
5766
- this.validateComponentStructure(index, result);
5767
- this.validateQueryStructure(index, result);
5768
- this.validateSettingsStructure(index, result);
5769
- } catch (error2) {
5770
- if (error2 instanceof z.ZodError) {
5771
- this.convertZodErrors(error2, result);
5772
- } else {
5773
- result.errors.push(
5774
- this.createError(
5775
- "STRUCTURAL_VALIDATION_FAILED",
5776
- "Schema structural validation failed",
5777
- "root",
5778
- "error",
5779
- "Check schema format and required properties"
5780
- )
5781
- );
5782
- }
5783
- }
5790
+ this.validateContentTypeStructure(index, result);
5791
+ this.validateComponentStructure(index, result);
5792
+ this.validateSettingsStructure(index, result);
5784
5793
  return result;
5785
5794
  }
5786
5795
  validateContentTypeStructure(index, result) {
5787
- if (!index.schema.contentTypes || index.contentTypes.ids.length === 0) {
5796
+ if (index.contentTypes.ids.length === 0) {
5788
5797
  result.warnings.push(
5789
5798
  this.createError(
5790
5799
  "NO_CONTENT_TYPES",
@@ -5796,17 +5805,6 @@ var init_dist2 = __esm({
5796
5805
  );
5797
5806
  }
5798
5807
  for (const [typeId, contentType] of index.contentTypes.entries) {
5799
- if (!contentType.fields || contentType.fields.length === 0 && !contentType.fieldsets) {
5800
- result.warnings.push(
5801
- this.createError(
5802
- "EMPTY_CONTENT_TYPE",
5803
- `Content type '${typeId}' has no fields`,
5804
- `contentTypes.${typeId}`,
5805
- "warning",
5806
- "Add fields to make this content type useful"
5807
- )
5808
- );
5809
- }
5810
5808
  this.validateFieldsStructure(
5811
5809
  contentType.fields || [],
5812
5810
  `contentTypes.${typeId}.fields`,
@@ -5816,7 +5814,7 @@ var init_dist2 = __esm({
5816
5814
  }
5817
5815
  validateComponentStructure(index, result) {
5818
5816
  for (const [componentId, component] of index.components.entries) {
5819
- if (!component.fields || component.fields.length === 0) {
5817
+ if (component.fields.length === 0) {
5820
5818
  result.errors.push(
5821
5819
  this.createError(
5822
5820
  "EMPTY_COMPONENT",
@@ -5831,147 +5829,23 @@ var init_dist2 = __esm({
5831
5829
  }
5832
5830
  }
5833
5831
  }
5834
- validateQueryStructure(index, result) {
5835
- for (const [queryId, query] of Object.entries(index.schema.queries || {})) {
5836
- if (!query.endpoint) {
5837
- result.errors.push(
5838
- this.createError(
5839
- "MISSING_QUERY_ENDPOINT",
5840
- `Query '${queryId}' is missing endpoint`,
5841
- `queries.${queryId}`,
5842
- "error",
5843
- "All queries must have an endpoint property"
5844
- )
5845
- );
5846
- }
5847
- const blocks = queryBlocksOf(query);
5848
- if (blocks.length === 0) {
5849
- result.errors.push(
5850
- this.createError(
5851
- "MISSING_QUERY_TYPE",
5852
- `Query '${queryId}' must have one of: ${QUERY_BLOCKS_SPELLED}`,
5853
- `queries.${queryId}`,
5854
- "error",
5855
- `Add a ${QUERY_BLOCKS_SPELLED} block to define the query operation`
5856
- )
5857
- );
5858
- }
5859
- if (query.fetch) {
5860
- this.validateFetchStructure(query.fetch, queryId, result);
5861
- }
5862
- if (query.aggregate) {
5863
- this.validateAggregateStructure(query.aggregate, queryId, Boolean(query.fetch), result);
5864
- }
5865
- if (query.search) {
5866
- this.validateSearchStructure(query.search, queryId, result);
5867
- }
5868
- }
5869
- }
5870
- validateFetchStructure(fetch2, queryId, result) {
5871
- const basePath = `queries.${queryId}.fetch`;
5872
- if (!fetch2.contentType && !fetch2.singleton) {
5873
- result.errors.push(
5874
- this.createError(
5875
- "FETCH_MISSING_TARGET",
5876
- `Fetch operation in query '${queryId}' must specify contentType or singleton`,
5877
- basePath,
5878
- "error",
5879
- "Add contentType or singleton property to the fetch operation"
5880
- )
5881
- );
5882
- }
5883
- if (fetch2.contentType && fetch2.singleton) {
5884
- result.errors.push(
5885
- this.createError(
5886
- "FETCH_MULTIPLE_TARGETS",
5887
- `Fetch operation in query '${queryId}' cannot have both contentType and singleton`,
5888
- basePath,
5889
- "error",
5890
- "Use either contentType OR singleton, not both"
5891
- )
5892
- );
5893
- }
5894
- }
5895
- validateAggregateStructure(aggregate, queryId, hasFetch, result) {
5896
- const basePath = `queries.${queryId}.aggregate`;
5897
- if (!hasFetch && !aggregate.source) {
5898
- result.errors.push(
5899
- this.createError(
5900
- "AGGREGATE_MISSING_SOURCE",
5901
- `Aggregate operation in query '${queryId}' must specify source content type when not used with fetch`,
5902
- `${basePath}.source`,
5903
- "error",
5904
- "Add source property to specify which content type to aggregate, or use with a fetch block"
5905
- )
5906
- );
5907
- }
5908
- }
5909
- validateSearchStructure(search, queryId, result) {
5910
- const basePath = `queries.${queryId}.search`;
5911
- if (!search.contentType && !search.contentTypes) {
5912
- result.errors.push(
5913
- this.createError(
5914
- "SEARCH_MISSING_CONTENT_TYPE",
5915
- `Search operation in query '${queryId}' must specify contentType or contentTypes`,
5916
- basePath,
5917
- "error",
5918
- "Add contentType for single type search, or contentTypes for multi-type/global search"
5919
- )
5920
- );
5921
- }
5922
- if (!search.query) {
5923
- result.errors.push(
5924
- this.createError(
5925
- "SEARCH_MISSING_QUERY",
5926
- `Search operation in query '${queryId}' must specify a search query`,
5927
- `${basePath}.query`,
5928
- "error",
5929
- 'Add query property with search string or parameter like "${q}"'
5930
- )
5931
- );
5932
- }
5933
- }
5934
5832
  validateSettingsStructure(index, result) {
5935
5833
  for (const [settingsId, settingsGroup] of index.settingsGroups.entries) {
5936
- if (!settingsGroup.fields || settingsGroup.fields.length === 0) {
5937
- result.warnings.push(
5938
- this.createError(
5939
- "EMPTY_SETTINGS_GROUP",
5940
- `Settings group '${settingsId}' has no fields`,
5941
- `settingsGroups.${settingsId}`,
5942
- "warning",
5943
- "Settings groups should have configuration fields"
5944
- )
5945
- );
5946
- } else {
5947
- this.validateFieldsStructure(
5948
- settingsGroup.fields,
5949
- `settingsGroups.${settingsId}.fields`,
5950
- result
5951
- );
5952
- }
5834
+ this.validateFieldsStructure(
5835
+ settingsGroup.fields,
5836
+ `settingsGroups.${settingsId}.fields`,
5837
+ result
5838
+ );
5953
5839
  }
5954
5840
  }
5955
5841
  validateFieldsStructure(fields, basePath, result) {
5956
- for (const [index, field] of fields.entries()) {
5957
- const fieldIdentifier = field.id || `[${index}]`;
5958
- const fieldPath = `${basePath}.${fieldIdentifier}`;
5959
- if (!field.id) {
5960
- result.errors.push(
5961
- this.createError(
5962
- "MISSING_FIELD_ID",
5963
- `Field at index ${index} is missing id`,
5964
- fieldPath,
5965
- "error",
5966
- "All fields must have an id property"
5967
- )
5968
- );
5969
- }
5842
+ for (const field of fields) {
5843
+ const fieldPath = `${basePath}.${field.id}`;
5970
5844
  if (!field.type) {
5971
5845
  result.errors.push(
5972
5846
  this.createError(
5973
5847
  "MISSING_FIELD_TYPE",
5974
- `Field '${field.id || `at index ${index}`}' is missing type`,
5848
+ `Field '${field.id}' is missing type`,
5975
5849
  fieldPath,
5976
5850
  "error",
5977
5851
  "All fields must have a type property"
@@ -6044,53 +5918,6 @@ var init_dist2 = __esm({
6044
5918
  }
6045
5919
  }
6046
5920
  }
6047
- convertZodErrors(zodError, result) {
6048
- for (const error2 of zodError.errors) {
6049
- const path25 = error2.path.join(".");
6050
- let message = error2.message;
6051
- let suggestion;
6052
- switch (error2.code) {
6053
- case "invalid_enum_value": {
6054
- const options = "options" in error2 ? error2.options : [];
6055
- message = `Invalid value. Expected one of: ${options.join(", ")}`;
6056
- suggestion = `Use one of the valid options: ${options.join(", ")}`;
6057
- break;
6058
- }
6059
- case "invalid_type": {
6060
- const expected = error2.expected;
6061
- const received = error2.received;
6062
- message = `Expected ${expected}, received ${received}`;
6063
- suggestion = `Change the value to be of type ${expected}`;
6064
- break;
6065
- }
6066
- case "too_small": {
6067
- const minimum = error2.minimum;
6068
- message = `Value is too small. Minimum: ${minimum}`;
6069
- suggestion = `Increase the value to at least ${minimum}`;
6070
- break;
6071
- }
6072
- case "too_big": {
6073
- const maximum = error2.maximum;
6074
- message = `Value is too large. Maximum: ${maximum}`;
6075
- suggestion = `Decrease the value to at most ${maximum}`;
6076
- break;
6077
- }
6078
- case "invalid_string": {
6079
- if (error2.validation === "email") {
6080
- message = "Invalid email format";
6081
- suggestion = "Provide a valid email address";
6082
- } else if (error2.validation === "url") {
6083
- message = "Invalid URL format";
6084
- suggestion = "Provide a valid URL starting with http:// or https://";
6085
- }
6086
- break;
6087
- }
6088
- }
6089
- result.errors.push(
6090
- this.createError("SCHEMA_VALIDATION_ERROR", message, path25 || "root", "error", suggestion)
6091
- );
6092
- }
6093
- }
6094
5921
  };
6095
5922
  FieldConfigValidator = class extends BaseValidator {
6096
5923
  feature = "fieldConfigurations";
@@ -6888,20 +6715,7 @@ var init_dist2 = __esm({
6888
6715
  }
6889
6716
  }
6890
6717
  } else {
6891
- if (contentTypeFields.has(field.id)) {
6892
- result.errors.push(
6893
- this.createError(
6894
- "DUPLICATE_FIELD_NAME",
6895
- `Duplicate field name '${field.id}' in content type '${contentTypeId}'`,
6896
- `contentTypes.${contentTypeId}.fields.${field.id}`,
6897
- "error",
6898
- "Field names must be unique within a content type",
6899
- true
6900
- )
6901
- );
6902
- } else {
6903
- contentTypeFields.set(field.id, field.type);
6904
- }
6718
+ contentTypeFields.set(field.id, field.type);
6905
6719
  }
6906
6720
  }
6907
6721
  }
@@ -6925,20 +6739,7 @@ var init_dist2 = __esm({
6925
6739
  for (const [componentId, component] of index.components.entries) {
6926
6740
  const fieldTypes = /* @__PURE__ */ new Map();
6927
6741
  if (component.fields)
6928
- for (const [index2, field] of component.fields.entries()) {
6929
- if (!field.id) {
6930
- result.errors.push(
6931
- this.createError(
6932
- "COMPONENT_FIELD_MISSING_ID",
6933
- `Field at index ${index2} in component '${componentId}' is missing id`,
6934
- `components.${componentId}.fields[${index2}]`,
6935
- "error",
6936
- "All component fields must have unique identifiers",
6937
- true
6938
- )
6939
- );
6940
- continue;
6941
- }
6742
+ for (const field of component.fields) {
6942
6743
  if (!field.type) {
6943
6744
  result.errors.push(
6944
6745
  this.createError(
@@ -6985,7 +6786,7 @@ var init_dist2 = __esm({
6985
6786
  }
6986
6787
  for (const [index2, field] of fields.entries()) {
6987
6788
  const fieldPath = `components.${componentId}.fields.${field.id || `[${index2}]`}`;
6988
- if (!field.id || !field.type) {
6789
+ if (!field.type) {
6989
6790
  result.errors.push(
6990
6791
  this.createError(
6991
6792
  "INCOMPLETE_FIELD_STRUCTURE",
@@ -7031,17 +6832,6 @@ var init_dist2 = __esm({
7031
6832
  if (component.fields)
7032
6833
  for (const field of component.fields) {
7033
6834
  const fieldPath = `components.${componentId}.fields.${field.id}`;
7034
- if (field.id && field.id.includes(".")) {
7035
- result.warnings.push(
7036
- this.createError(
7037
- "FIELD_NAME_SERIALIZATION_RISK",
7038
- `Field '${field.id}' in component '${componentId}' contains dots which may cause serialization issues`,
7039
- fieldPath,
7040
- "warning",
7041
- "Use underscores or camelCase instead of dots in field names"
7042
- )
7043
- );
7044
- }
7045
6835
  if (field.id && field.id.length > 50) {
7046
6836
  result.warnings.push(
7047
6837
  this.createError(
@@ -7103,22 +6893,6 @@ var init_dist2 = __esm({
7103
6893
  for (const [componentId, component] of index.components.entries) {
7104
6894
  const usagePattern = componentUsagePatterns[componentId];
7105
6895
  if (!usagePattern) continue;
7106
- if (this.isSeoComponent(componentId, component)) {
7107
- for (const contentTypeId of usagePattern.usedInContentTypes) {
7108
- const contentType = index.schema.contentTypes?.[contentTypeId];
7109
- if (contentType && this.isNonPublicContentType(contentType)) {
7110
- result.warnings.push(
7111
- this.createError(
7112
- "SEO_COMPONENT_IN_PRIVATE_CONTENT",
7113
- `SEO component '${componentId}' is used in non-public content type '${contentTypeId}'`,
7114
- `contentTypes.${contentTypeId}`,
7115
- "warning",
7116
- "Consider whether SEO metadata is needed for internal/private content types"
7117
- )
7118
- );
7119
- }
7120
- }
7121
- }
7122
6896
  if (this.isSystemComponent(componentId, component)) {
7123
6897
  for (const contentTypeId of usagePattern.usedInContentTypes) {
7124
6898
  result.warnings.push(
@@ -7278,20 +7052,6 @@ var init_dist2 = __esm({
7278
7052
  }
7279
7053
  return patterns;
7280
7054
  }
7281
- isSeoComponent(componentId, component) {
7282
- const seoKeywords = ["seo", "meta", "og", "twitter", "social"];
7283
- const componentName = componentId.toLowerCase();
7284
- return seoKeywords.some((keyword) => componentName.includes(keyword)) || (component.fields || []).some(
7285
- (field) => field.id && seoKeywords.some((keyword) => field.id.toLowerCase().includes(keyword))
7286
- );
7287
- }
7288
- isNonPublicContentType(contentType) {
7289
- const internalKeywords = ["admin", "system", "internal", "private", "draft"];
7290
- const ct = contentType;
7291
- const typeName = ct.id?.toLowerCase() || "";
7292
- const visibility = ct.metadata?.visibility;
7293
- return internalKeywords.some((keyword) => typeName.includes(keyword)) || visibility === "private";
7294
- }
7295
7055
  isSystemComponent(componentId, _component) {
7296
7056
  const systemKeywords = ["system", "admin", "internal", "config"];
7297
7057
  return systemKeywords.some((keyword) => componentId.toLowerCase().includes(keyword));
@@ -7305,15 +7065,12 @@ var init_dist2 = __esm({
7305
7065
  );
7306
7066
  return componentField ? {
7307
7067
  fieldName: componentField.id,
7308
- hasRules: (componentField.rules || []).length > 0,
7309
- isRequired: componentField.required || false
7068
+ hasRules: (componentField.rules || []).length > 0
7310
7069
  } : null;
7311
7070
  }).filter(Boolean);
7312
7071
  if (usagePatterns.length < 2) return false;
7313
7072
  const firstPattern = usagePatterns[0];
7314
- return usagePatterns.some(
7315
- (pattern) => pattern && (pattern.hasRules !== firstPattern?.hasRules || pattern.isRequired !== firstPattern?.isRequired)
7316
- );
7073
+ return usagePatterns.some((pattern) => pattern && pattern.hasRules !== firstPattern?.hasRules);
7317
7074
  }
7318
7075
  };
7319
7076
  ComponentNestingValidator = class extends BaseValidator {
@@ -8641,13 +8398,36 @@ var init_dist2 = __esm({
8641
8398
  settingsGroups: "settings groups"
8642
8399
  };
8643
8400
  CROSS_VERSION_CHECKS = {
8644
- FIELD_TYPE_CHANGE_FORBIDDEN: "a field's type change",
8645
- REFERENCE_TARGET_REMOVED: "a removed reference target",
8646
- MISSING_DEPRECATION_MARKER: "a replacement field beside an undeprecated one"
8401
+ FIELD_TYPE_CHANGE_FORBIDDEN: {
8402
+ names: "a field's type change",
8403
+ severity: "error",
8404
+ fixable: false
8405
+ },
8406
+ REFERENCE_TARGET_REMOVED: {
8407
+ names: "a removed reference target",
8408
+ severity: "error",
8409
+ fixable: false
8410
+ },
8411
+ MISSING_DEPRECATION_MARKER: {
8412
+ names: "a replacement field beside an undeprecated one",
8413
+ severity: "warning",
8414
+ fixable: true
8415
+ }
8647
8416
  };
8417
+ CROSS_VERSION_CODES = Object.keys(CROSS_VERSION_CHECKS);
8648
8418
  EvolutionValidator = class extends BaseValidator {
8649
8419
  feature = "schemaEvolution";
8650
8420
  schemaDiffer = new SchemaDiffer();
8421
+ /**
8422
+ * Each cross-version check's finder, under the code {@link CROSS_VERSION_CHECKS} reports it by. A
8423
+ * code the table gains is a missing property here until it has a finder, and a finder under a code
8424
+ * the table does not name is an excess one.
8425
+ */
8426
+ crossVersionFinders = {
8427
+ FIELD_TYPE_CHANGE_FORBIDDEN: (comparison) => this.fieldTypeChanges(comparison),
8428
+ REFERENCE_TARGET_REMOVED: (comparison) => this.removedReferenceTargets(comparison),
8429
+ MISSING_DEPRECATION_MARKER: (comparison) => this.undeprecatedReplacements(comparison)
8430
+ };
8651
8431
  async validate(index) {
8652
8432
  const result = this.createResult();
8653
8433
  this.validateEvolutionReadiness(index, result);
@@ -8660,13 +8440,22 @@ var init_dist2 = __esm({
8660
8440
  const result = this.createResult();
8661
8441
  const newIndex = buildSchemaIndex(newSchema);
8662
8442
  this.validateEvolutionReadiness(newIndex, result);
8663
- this.validateFieldTypeImmutability(oldSchema, newSchema, result);
8664
- this.validateDeprecationWorkflow(oldSchema, newSchema, result);
8665
- this.validateReferenceTargetRemovals(oldSchema, newIndex, result);
8443
+ const comparison = {
8444
+ previous: oldSchema,
8445
+ next: newIndex,
8446
+ diff: this.schemaDiffer.computeDiff(oldSchema, newSchema)
8447
+ };
8448
+ for (const code of CROSS_VERSION_CODES) {
8449
+ const { severity, fixable } = CROSS_VERSION_CHECKS[code];
8450
+ const reported = severity === "error" ? result.errors : result.warnings;
8451
+ for (const { message, path: path25, suggestion } of this.crossVersionFinders[code](comparison)) {
8452
+ reported.push(this.createError(code, message, path25, severity, suggestion, fixable));
8453
+ }
8454
+ }
8666
8455
  return result;
8667
8456
  }
8668
8457
  /**
8669
- * Flags reference fields that still target a content/asset type removed in
8458
+ * Reference fields that still target a content/asset type removed in
8670
8459
  * this change. `DataIntegrityValidator` already errors on a dangling target
8671
8460
  * (REFERENCE_TARGET_NOT_FOUND) when it validates the new schema in isolation;
8672
8461
  * this diff-aware pass upgrades the same situation to an attributed breaking
@@ -8676,99 +8465,81 @@ var init_dist2 = __esm({
8676
8465
  * Reference fields inside asset types are covered too — {@link BaseValidator.traverseFields}
8677
8466
  * walks asset type fields by default.
8678
8467
  */
8679
- validateReferenceTargetRemovals(oldSchema, newIndex, result) {
8680
- const diff2 = this.schemaDiffer.computeDiff(oldSchema, newIndex.schema);
8468
+ removedReferenceTargets({ next, diff: diff2 }) {
8681
8469
  const removedTargets = /* @__PURE__ */ new Map();
8682
8470
  for (const id of diff2.removed.contentTypes) removedTargets.set(id, "content");
8683
8471
  for (const id of diff2.removed.singletonContentTypes) removedTargets.set(id, "content");
8684
8472
  for (const id of diff2.removed.assetTypes) removedTargets.set(id, "asset");
8685
- if (removedTargets.size === 0) return;
8686
- this.traverseFields(newIndex, (field, path25) => {
8473
+ const findings = [];
8474
+ if (removedTargets.size === 0) return findings;
8475
+ this.traverseFields(next, (field, path25) => {
8687
8476
  if (field.type !== "reference") return;
8688
8477
  const targets = normalizeReferenceTargets(field.config?.to);
8689
8478
  for (const [index, targetId] of targets.entries()) {
8690
8479
  const family = removedTargets.get(targetId);
8691
8480
  if (!family) continue;
8692
- result.errors.push(
8693
- this.createError(
8694
- "REFERENCE_TARGET_REMOVED",
8695
- `Reference field '${field.id}' targets ${family} type '${targetId}', which was removed in this change`,
8696
- `${path25}.config.to[${index}]`,
8697
- "error",
8698
- `Restore the ${family} type '${targetId}', or remove/repoint the reference field '${field.id}' before deploying`,
8699
- false
8700
- )
8701
- );
8481
+ findings.push({
8482
+ message: `Reference field '${field.id}' targets ${family} type '${targetId}', which was removed in this change`,
8483
+ path: `${path25}.config.to[${index}]`,
8484
+ suggestion: `Restore the ${family} type '${targetId}', or remove/repoint the reference field '${field.id}' before deploying`
8485
+ });
8702
8486
  }
8703
8487
  });
8488
+ return findings;
8704
8489
  }
8705
8490
  /**
8706
- * Enforces the field type immutability rule
8491
+ * The field type immutability rule: a field keeps the type it was deployed with.
8707
8492
  */
8708
- validateFieldTypeImmutability(oldSchema, newSchema, result) {
8709
- const oldFieldTypes = this.buildFieldTypeHistory(oldSchema);
8710
- const newFieldTypes = this.buildFieldTypeHistory(newSchema);
8493
+ fieldTypeChanges({ previous, next }) {
8494
+ const oldFieldTypes = this.buildFieldTypeHistory(previous);
8495
+ const newFieldTypes = this.buildFieldTypeHistory(next.schema);
8496
+ const findings = [];
8711
8497
  for (const [fieldKey, oldField] of oldFieldTypes.entries()) {
8712
8498
  const newField = newFieldTypes.get(fieldKey);
8713
8499
  if (newField && oldField.fieldType !== newField.fieldType) {
8714
8500
  const oldType = this.normalizeFieldType(oldField.fieldType);
8715
8501
  const newType = this.normalizeFieldType(newField.fieldType);
8716
- result.errors.push(
8717
- this.createError(
8718
- "FIELD_TYPE_CHANGE_FORBIDDEN",
8719
- `Field type cannot be changed after deployment: "${oldField.fieldId}" (${oldType} \u2192 ${newType})`,
8720
- newField.path,
8721
- "error",
8722
- `Use deprecation workflow:
8502
+ findings.push({
8503
+ message: `Field type cannot be changed after deployment: "${oldField.fieldId}" (${oldType} \u2192 ${newType})`,
8504
+ path: newField.path,
8505
+ suggestion: `Use deprecation workflow:
8723
8506
  \u250C\u2500 Correct approach
8724
8507
  \u2502 fields:
8725
8508
  \u2502 \u251C\u2500 ${oldField.fieldId}:
8726
8509
  \u2502 \u2502 type: "${oldField.fieldType}"
8727
8510
  \u2502 \u2502 deprecated: true # Mark old field as deprecated
8728
8511
  \u2502 \u2514\u2500 ${oldField.fieldId}_v2:
8729
- \u2502 type: "${newField.fieldType}" # Create new field with correct type`,
8730
- false
8731
- )
8732
- );
8512
+ \u2502 type: "${newField.fieldType}" # Create new field with correct type`
8513
+ });
8733
8514
  }
8734
8515
  }
8516
+ return findings;
8735
8517
  }
8736
8518
  /**
8737
- * Validates proper deprecation workflow when introducing new fields
8519
+ * A field added beside one it appears to replace, which is not marked deprecated.
8738
8520
  */
8739
- validateDeprecationWorkflow(oldSchema, newSchema, result) {
8740
- const diff2 = this.schemaDiffer.computeDiff(oldSchema, newSchema);
8521
+ undeprecatedReplacements({ previous, next, diff: diff2 }) {
8522
+ const findings = [];
8741
8523
  for (const contentTypeChange of diff2.modified.contentTypes) {
8742
8524
  for (const addedFieldId of contentTypeChange.changes.addedFields) {
8743
8525
  const potentialOriginalField = this.findPotentialOriginalField(
8744
8526
  addedFieldId,
8745
- oldSchema,
8527
+ previous,
8746
8528
  contentTypeChange.name
8747
8529
  );
8748
- if (potentialOriginalField) {
8749
- const isOriginalDeprecated = this.isFieldDeprecated(
8750
- potentialOriginalField,
8751
- newSchema,
8752
- contentTypeChange.name
8753
- );
8754
- if (!isOriginalDeprecated) {
8755
- result.warnings.push(
8756
- this.createError(
8757
- "MISSING_DEPRECATION_MARKER",
8758
- `Field '${addedFieldId}' appears to be a replacement for '${potentialOriginalField}'. Consider marking the original field as deprecated.`,
8759
- `contentTypes.${contentTypeChange.name}.fields.${potentialOriginalField}`,
8760
- "warning",
8761
- `Mark the original field as deprecated:
8530
+ if (potentialOriginalField && !this.isFieldDeprecated(potentialOriginalField, next.schema, contentTypeChange.name)) {
8531
+ findings.push({
8532
+ message: `Field '${addedFieldId}' appears to be a replacement for '${potentialOriginalField}'. Consider marking the original field as deprecated.`,
8533
+ path: `contentTypes.${contentTypeChange.name}.fields.${potentialOriginalField}`,
8534
+ suggestion: `Mark the original field as deprecated:
8762
8535
  - id: "${potentialOriginalField}"
8763
8536
  type: "..."
8764
- deprecated: true`,
8765
- true
8766
- )
8767
- );
8768
- }
8537
+ deprecated: true`
8538
+ });
8769
8539
  }
8770
8540
  }
8771
8541
  }
8542
+ return findings;
8772
8543
  }
8773
8544
  /**
8774
8545
  * Builds a map of field types from a schema for comparison
@@ -9013,9 +8784,7 @@ var init_dist2 = __esm({
9013
8784
  const safeChanges = [];
9014
8785
  const evolutionResult = await this.validateSchemaEvolution(oldSchema, newSchema);
9015
8786
  for (const error2 of evolutionResult.errors) {
9016
- if (error2.code === "FIELD_TYPE_CHANGE_FORBIDDEN" || error2.code === "REFERENCE_TARGET_REMOVED") {
9017
- breakingChanges.push(error2.message);
9018
- }
8787
+ if (isCrossVersionCheckCode(error2.code)) breakingChanges.push(error2.message);
9019
8788
  }
9020
8789
  const diff2 = this.schemaDiffer.computeDiff(oldSchema, newSchema);
9021
8790
  const compatibility = classifyCompatibility(diff2);
@@ -9226,19 +8995,6 @@ var init_dist2 = __esm({
9226
8995
  return fieldIds;
9227
8996
  }
9228
8997
  validateFieldRules(rules, fieldId, fieldPath, result, allFieldIds, _context) {
9229
- if (!Array.isArray(rules)) {
9230
- result.errors.push(
9231
- this.createError(
9232
- "INVALID_RULES_FORMAT",
9233
- "Field rules must be an array",
9234
- `${fieldPath}.rules`,
9235
- "error",
9236
- "Change rules to an array format: rules: [...]",
9237
- true
9238
- )
9239
- );
9240
- return;
9241
- }
9242
8998
  const ruleTypes = /* @__PURE__ */ new Set();
9243
8999
  for (const [ruleIndex, rule] of rules.entries()) {
9244
9000
  const rulePath = `${fieldPath}.rules[${ruleIndex}]`;
@@ -9261,86 +9017,16 @@ var init_dist2 = __esm({
9261
9017
  this.checkCircularDependencies(rules, fieldId, fieldPath, result, allFieldIds);
9262
9018
  this.validateRuleLogicConsistency(rules, fieldId, fieldPath, result);
9263
9019
  }
9020
+ /**
9021
+ * A rule's shape — its type, its action, its message, a mode, a non-empty list of conditions and
9022
+ * each condition's operator — is the parse's (`FieldRuleSchema`), which refuses every one this
9023
+ * checked; none of those eleven checks could fire (REL-1497, `parse-refuses-first.test.ts`). What
9024
+ * is left is what the parse cannot know: which fields a condition names, and whether its operator
9025
+ * has the value it compares with.
9026
+ */
9264
9027
  validateSingleRule(rule, rulePath, result, allFieldIds, currentFieldId) {
9265
- const validRuleTypes = ["visibility", "requirement", "validation", "readOnly"];
9266
- if (!validRuleTypes.includes(rule.type)) {
9267
- result.errors.push(
9268
- this.createError(
9269
- "INVALID_RULE_TYPE",
9270
- `Invalid rule type '${rule.type}'. Must be one of: ${validRuleTypes.join(", ")}`,
9271
- `${rulePath}.type`,
9272
- "error",
9273
- `Use one of the valid rule types: ${validRuleTypes.join(", ")}`,
9274
- true
9275
- )
9276
- );
9277
- }
9278
- const hasConditions = rule.conditions && rule.conditions.conditions && rule.conditions.conditions.length > 0;
9279
- if (!hasConditions) {
9280
- result.errors.push(
9281
- this.createError(
9282
- "MISSING_RULE_CONDITION",
9283
- "Rule must specify conditions with at least one condition entry",
9284
- rulePath,
9285
- "error",
9286
- "Add a conditions object to define when the rule applies",
9287
- true
9288
- )
9289
- );
9290
- }
9291
- if (hasConditions) {
9292
- this.validateConditions(
9293
- rule.conditions,
9294
- `${rulePath}.conditions`,
9295
- result,
9296
- allFieldIds,
9297
- currentFieldId
9298
- );
9299
- }
9300
- this.validateRuleSpecificProperties(rule, rulePath, result);
9301
- }
9302
- validateConditions(conditions, conditionsPath, result, allFieldIds, currentFieldId) {
9303
- if (!conditions) return;
9304
- const validModes = ["all", "any", "none"];
9305
- if (!validModes.includes(conditions.mode)) {
9306
- result.errors.push(
9307
- this.createError(
9308
- "INVALID_CONDITIONS_MODE",
9309
- `Invalid conditions mode '${conditions.mode}'. Must be one of: ${validModes.join(", ")}`,
9310
- `${conditionsPath}.mode`,
9311
- "error",
9312
- `Use one of: ${validModes.join(", ")}`,
9313
- true
9314
- )
9315
- );
9316
- }
9317
- if (!conditions.conditions || !Array.isArray(conditions.conditions)) {
9318
- result.errors.push(
9319
- this.createError(
9320
- "INVALID_CONDITIONS_ARRAY",
9321
- "Conditions must include a 'conditions' array",
9322
- `${conditionsPath}.conditions`,
9323
- "error",
9324
- "Add a conditions array with at least one condition",
9325
- true
9326
- )
9327
- );
9328
- return;
9329
- }
9330
- if (conditions.conditions.length === 0) {
9331
- result.errors.push(
9332
- this.createError(
9333
- "EMPTY_CONDITIONS_ARRAY",
9334
- "Conditions array cannot be empty",
9335
- `${conditionsPath}.conditions`,
9336
- "error",
9337
- "Add at least one condition to the array",
9338
- true
9339
- )
9340
- );
9341
- return;
9342
- }
9343
- for (const [index, condition] of conditions.conditions.entries()) {
9028
+ const conditionsPath = `${rulePath}.conditions`;
9029
+ for (const [index, condition] of rule.conditions.conditions.entries()) {
9344
9030
  this.validateSingleCondition(
9345
9031
  condition,
9346
9032
  `${conditionsPath}.conditions[${index}]`,
@@ -9387,28 +9073,6 @@ var init_dist2 = __esm({
9387
9073
  )
9388
9074
  );
9389
9075
  }
9390
- const validOperators = [
9391
- "equals",
9392
- "not_equals",
9393
- "contains",
9394
- "not_contains",
9395
- "greater_than",
9396
- "less_than",
9397
- "is_empty",
9398
- "is_not_empty"
9399
- ];
9400
- if (!validOperators.includes(condition.operator)) {
9401
- result.errors.push(
9402
- this.createError(
9403
- "INVALID_CONDITION_OPERATOR",
9404
- `Invalid operator '${condition.operator}'. Must be one of: ${validOperators.join(", ")}`,
9405
- `${conditionPath}.operator`,
9406
- "error",
9407
- `Use one of: ${validOperators.join(", ")}`,
9408
- true
9409
- )
9410
- );
9411
- }
9412
9076
  const operatorsRequiringValue = [
9413
9077
  "equals",
9414
9078
  "not_equals",
@@ -9444,70 +9108,6 @@ var init_dist2 = __esm({
9444
9108
  );
9445
9109
  }
9446
9110
  }
9447
- validateRuleSpecificProperties(rule, rulePath, result) {
9448
- switch (rule.type) {
9449
- case "visibility": {
9450
- if ("action" in rule && rule.action && !["show", "hide"].includes(rule.action)) {
9451
- result.errors.push(
9452
- this.createError(
9453
- "INVALID_VISIBILITY_ACTION",
9454
- `Invalid visibility action '${rule.action}'. Must be 'show' or 'hide'`,
9455
- `${rulePath}.action`,
9456
- "error",
9457
- "Use 'show' or 'hide'",
9458
- true
9459
- )
9460
- );
9461
- }
9462
- break;
9463
- }
9464
- case "requirement": {
9465
- if ("action" in rule && rule.action && !["require", "optional"].includes(rule.action)) {
9466
- result.errors.push(
9467
- this.createError(
9468
- "INVALID_REQUIREMENT_ACTION",
9469
- `Invalid requirement action '${rule.action}'. Must be 'require' or 'optional'`,
9470
- `${rulePath}.action`,
9471
- "error",
9472
- "Use 'require' or 'optional'",
9473
- true
9474
- )
9475
- );
9476
- }
9477
- break;
9478
- }
9479
- case "validation": {
9480
- if ("message" in rule && rule.message && typeof rule.message !== "string") {
9481
- result.errors.push(
9482
- this.createError(
9483
- "INVALID_VALIDATION_MESSAGE",
9484
- "Validation message must be a string",
9485
- `${rulePath}.message`,
9486
- "error",
9487
- "Provide a string message for validation errors",
9488
- true
9489
- )
9490
- );
9491
- }
9492
- break;
9493
- }
9494
- case "readOnly": {
9495
- if ("action" in rule && rule.action && !["enable", "disable"].includes(rule.action)) {
9496
- result.errors.push(
9497
- this.createError(
9498
- "INVALID_READONLY_ACTION",
9499
- `Invalid readOnly action '${rule.action}'. Must be 'enable' or 'disable'`,
9500
- `${rulePath}.action`,
9501
- "error",
9502
- "Use 'enable' or 'disable'",
9503
- true
9504
- )
9505
- );
9506
- }
9507
- break;
9508
- }
9509
- }
9510
- }
9511
9111
  checkCircularDependencies(rules, _fieldId, _fieldPath, _result, _allFieldIds) {
9512
9112
  const referencedFields = /* @__PURE__ */ new Set();
9513
9113
  for (const rule of rules) {
@@ -9582,6 +9182,15 @@ var init_dist2 = __esm({
9582
9182
  });
9583
9183
  return fieldIds;
9584
9184
  }
9185
+ /**
9186
+ * A declared `localization`, in the one of its three forms the parse admits
9187
+ * (`LocalizationConfigSchema`): a boolean, a list of locale codes, or `{ enabled, requiredLocales? }`
9188
+ * with `enabled` a boolean and `requiredLocales` a list. This refused any other form
9189
+ * (`INVALID_LOCALIZATION_FORMAT`), an `enabled` that is not a boolean
9190
+ * (`INVALID_LOCALIZATION_ENABLED`) and a `requiredLocales` that is not a list
9191
+ * (`INVALID_REQUIRED_LOCALES`) — the parse refuses all three first, so none could fire (REL-1497,
9192
+ * `parse-refuses-first.test.ts`).
9193
+ */
9585
9194
  validateFieldLocalization(field, fieldPath, result, _allFieldIds, _context) {
9586
9195
  const localization = field.localization;
9587
9196
  const localizationPath = `${fieldPath}.localization`;
@@ -9589,24 +9198,8 @@ var init_dist2 = __esm({
9589
9198
  this.validateBooleanLocalization(localization, localizationPath, result, field);
9590
9199
  } else if (Array.isArray(localization)) {
9591
9200
  this.validateArrayLocalization(localization, localizationPath, result, field);
9592
- } else if (typeof localization === "object" && localization !== null) {
9593
- this.validateObjectLocalization(
9594
- localization,
9595
- localizationPath,
9596
- result,
9597
- field
9598
- );
9599
- } else {
9600
- result.errors.push(
9601
- this.createError(
9602
- "INVALID_LOCALIZATION_FORMAT",
9603
- "Localization must be a boolean, string array, or configuration object",
9604
- localizationPath,
9605
- "error",
9606
- "Use true/false, array of locale codes, or { enabled: boolean, ... } object",
9607
- true
9608
- )
9609
- );
9201
+ } else if (localization !== void 0) {
9202
+ this.validateRequiredLocales(localization.requiredLocales, localizationPath, result);
9610
9203
  }
9611
9204
  }
9612
9205
  validateBooleanLocalization(enabled, localizationPath, result, field) {
@@ -9664,36 +9257,10 @@ var init_dist2 = __esm({
9664
9257
  );
9665
9258
  }
9666
9259
  }
9667
- validateObjectLocalization(config, localizationPath, result, _field) {
9668
- if (config.enabled !== void 0 && typeof config.enabled !== "boolean") {
9669
- result.errors.push(
9670
- this.createError(
9671
- "INVALID_LOCALIZATION_ENABLED",
9672
- "Localization 'enabled' property must be a boolean",
9673
- `${localizationPath}.enabled`,
9674
- "error",
9675
- "Use true or false",
9676
- true
9677
- )
9678
- );
9679
- }
9680
- if (config.requiredLocales) {
9681
- if (Array.isArray(config.requiredLocales)) {
9682
- for (const [index, locale] of config.requiredLocales.entries()) {
9683
- this.validateLocaleCode(locale, `${localizationPath}.requiredLocales[${index}]`, result);
9684
- }
9685
- } else {
9686
- result.errors.push(
9687
- this.createError(
9688
- "INVALID_REQUIRED_LOCALES",
9689
- "Required locales must be an array of locale codes",
9690
- `${localizationPath}.requiredLocales`,
9691
- "error",
9692
- "Use array format: ['en-US', 'fr-FR']",
9693
- true
9694
- )
9695
- );
9696
- }
9260
+ validateRequiredLocales(requiredLocales, localizationPath, result) {
9261
+ if (requiredLocales === void 0) return;
9262
+ for (const [index, locale] of requiredLocales.entries()) {
9263
+ this.validateLocaleCode(locale, `${localizationPath}.requiredLocales[${index}]`, result);
9697
9264
  }
9698
9265
  }
9699
9266
  validateFieldFallback(field, fieldPath, result, allFieldIds, _context) {
@@ -10080,93 +9647,13 @@ var init_dist2 = __esm({
10080
9647
  });
10081
9648
  }
10082
9649
  /**
10083
- * Validate locale configuration for consistency and best practices
9650
+ * The locale list's shape — exactly one default, unique codes, fallbacks to declared locales,
9651
+ * none to itself and no cycle — is the parse's (`LocalesArraySchema`), which refuses each first,
9652
+ * so the six checks this made of it could not fire (REL-1497, `parse-refuses-first.test.ts`).
9653
+ * What is left is advice the parse does not give.
10084
9654
  */
10085
9655
  validateLocaleConfiguration(locales, result) {
10086
- const defaultLocales = locales.filter((l) => l.default === true);
10087
- if (defaultLocales.length === 0) {
10088
- result.errors.push(
10089
- this.createError(
10090
- "NO_DEFAULT_LOCALE",
10091
- "No default locale defined. Exactly one locale must be marked as default.",
10092
- "locales",
10093
- "error",
10094
- "Add 'default: true' to one of your locale definitions",
10095
- true
10096
- )
10097
- );
10098
- } else if (defaultLocales.length > 1) {
10099
- result.errors.push(
10100
- this.createError(
10101
- "MULTIPLE_DEFAULT_LOCALES",
10102
- `Multiple default locales defined: ${defaultLocales.map((l) => l.code).join(", ")}`,
10103
- "locales",
10104
- "error",
10105
- "Only one locale can be the default",
10106
- true
10107
- )
10108
- );
10109
- }
10110
- const seenCodes = /* @__PURE__ */ new Set();
10111
- for (const [index, locale] of locales.entries()) {
10112
- if (seenCodes.has(locale.code)) {
10113
- result.errors.push(
10114
- this.createError(
10115
- "DUPLICATE_LOCALE_CODE",
10116
- `Duplicate locale code '${locale.code}'`,
10117
- `locales[${index}]`,
10118
- "error",
10119
- "Each locale must have a unique code",
10120
- true
10121
- )
10122
- );
10123
- }
10124
- seenCodes.add(locale.code);
10125
- }
10126
- const localeCodes = new Set(locales.map((l) => l.code));
10127
- for (const [index, locale] of locales.entries()) {
10128
- if (!locale.fallback) continue;
10129
- for (const [fallbackIndex, fallbackCode] of locale.fallback.entries()) {
10130
- if (!localeCodes.has(fallbackCode)) {
10131
- result.errors.push(
10132
- this.createError(
10133
- "INVALID_LOCALE_FALLBACK",
10134
- `Locale '${locale.code}' has fallback to non-existent locale '${fallbackCode}'`,
10135
- `locales[${index}].fallback[${fallbackIndex}]`,
10136
- "error",
10137
- `Reference an existing locale: ${[...localeCodes].join(", ")}`,
10138
- true
10139
- )
10140
- );
10141
- }
10142
- if (fallbackCode === locale.code) {
10143
- result.errors.push(
10144
- this.createError(
10145
- "SELF_REFERENCING_LOCALE_FALLBACK",
10146
- `Locale '${locale.code}' references itself in fallback chain`,
10147
- `locales[${index}].fallback[${fallbackIndex}]`,
10148
- "error",
10149
- "Remove self-reference from fallback chain",
10150
- true
10151
- )
10152
- );
10153
- }
10154
- }
10155
- const circularPath = this.detectCircularFallback(locale.code, locales);
10156
- if (circularPath) {
10157
- result.errors.push(
10158
- this.createError(
10159
- "CIRCULAR_LOCALE_FALLBACK",
10160
- `Circular fallback chain detected: ${circularPath.join(" \u2192 ")}`,
10161
- `locales[${index}].fallback`,
10162
- "error",
10163
- "Remove circular references in fallback chain",
10164
- true
10165
- )
10166
- );
10167
- }
10168
- }
10169
- const defaultLocale = defaultLocales[0];
9656
+ const defaultLocale = locales.find((l) => l.default === true);
10170
9657
  if (defaultLocale?.fallback && defaultLocale.fallback.length > 0) {
10171
9658
  result.warnings.push(
10172
9659
  this.createError(
@@ -10224,34 +9711,6 @@ var init_dist2 = __esm({
10224
9711
  }
10225
9712
  });
10226
9713
  }
10227
- /**
10228
- * Detects circular fallback chains starting from a given locale
10229
- */
10230
- detectCircularFallback(startCode, locales) {
10231
- const visited = /* @__PURE__ */ new Set();
10232
- const path25 = [];
10233
- const localeMap = new Map(locales.map((l) => [l.code, l]));
10234
- const visit = (code) => {
10235
- if (visited.has(code)) {
10236
- const cycleStart = path25.indexOf(code);
10237
- return [...path25.slice(cycleStart), code];
10238
- }
10239
- const locale = localeMap.get(code);
10240
- if (!locale?.fallback || locale.fallback.length === 0) {
10241
- return null;
10242
- }
10243
- visited.add(code);
10244
- path25.push(code);
10245
- for (const fallbackCode of locale.fallback) {
10246
- const cycle = visit(fallbackCode);
10247
- if (cycle) return cycle;
10248
- }
10249
- path25.pop();
10250
- visited.delete(code);
10251
- return null;
10252
- };
10253
- return visit(startCode);
10254
- }
10255
9714
  };
10256
9715
  SettingsGroupsValidator = class extends BaseValidator {
10257
9716
  feature = "settingsGroups";
@@ -10328,53 +9787,27 @@ var init_dist2 = __esm({
10328
9787
  )
10329
9788
  );
10330
9789
  }
10331
- if (!settingsGroup.displayName || typeof settingsGroup.displayName !== "string") {
10332
- result.errors.push(
9790
+ if (settingsGroup.displayName.length < 3) {
9791
+ result.warnings.push(
10333
9792
  this.createError(
10334
- "MISSING_SETTINGS_GROUP_DISPLAY_NAME",
10335
- `Settings group '${groupId}' must have a displayName`,
9793
+ "SHORT_SETTINGS_GROUP_DISPLAY_NAME",
9794
+ `Settings group '${groupId}' has a very short displayName`,
10336
9795
  `${groupPath}.displayName`,
10337
- "error",
10338
- "Add a descriptive displayName property",
10339
- true
9796
+ "warning",
9797
+ "Use a more descriptive displayName (at least 3 characters)"
10340
9798
  )
10341
9799
  );
10342
- } else {
10343
- if (settingsGroup.displayName.length < 3) {
10344
- result.warnings.push(
10345
- this.createError(
10346
- "SHORT_SETTINGS_GROUP_DISPLAY_NAME",
10347
- `Settings group '${groupId}' has a very short displayName`,
10348
- `${groupPath}.displayName`,
10349
- "warning",
10350
- "Use a more descriptive displayName (at least 3 characters)"
10351
- )
10352
- );
10353
- }
10354
9800
  }
10355
9801
  if (settingsGroup.description) {
10356
- if (typeof settingsGroup.description === "string") {
10357
- stats.groupsWithDescriptions++;
10358
- if (settingsGroup.description.length < 10) {
10359
- result.warnings.push(
10360
- this.createError(
10361
- "SHORT_SETTINGS_GROUP_DESCRIPTION",
10362
- `Settings group '${groupId}' has a very short description`,
10363
- `${groupPath}.description`,
10364
- "warning",
10365
- "Provide a more detailed description for better understanding"
10366
- )
10367
- );
10368
- }
10369
- } else {
10370
- result.errors.push(
9802
+ stats.groupsWithDescriptions++;
9803
+ if (settingsGroup.description.length < 10) {
9804
+ result.warnings.push(
10371
9805
  this.createError(
10372
- "INVALID_SETTINGS_GROUP_DESCRIPTION",
10373
- `Settings group '${groupId}' description must be a string`,
9806
+ "SHORT_SETTINGS_GROUP_DESCRIPTION",
9807
+ `Settings group '${groupId}' has a very short description`,
10374
9808
  `${groupPath}.description`,
10375
- "error",
10376
- "Use a string description",
10377
- true
9809
+ "warning",
9810
+ "Provide a more detailed description for better understanding"
10378
9811
  )
10379
9812
  );
10380
9813
  }
@@ -10389,47 +9822,13 @@ var init_dist2 = __esm({
10389
9822
  )
10390
9823
  );
10391
9824
  }
10392
- if (!settingsGroup.fields || !Array.isArray(settingsGroup.fields)) {
10393
- result.errors.push(
10394
- this.createError(
10395
- "MISSING_SETTINGS_FIELDS_ARRAY",
10396
- `Settings group '${groupId}' must have a fields array`,
10397
- `${groupPath}.fields`,
10398
- "error",
10399
- "Add a fields array with field configurations",
10400
- true
10401
- )
10402
- );
10403
- } else if (settingsGroup.fields.length === 0) {
10404
- result.warnings.push(
10405
- this.createError(
10406
- "EMPTY_SETTINGS_GROUP_FIELDS",
10407
- `Settings group '${groupId}' has no fields`,
10408
- `${groupPath}.fields`,
10409
- "warning",
10410
- "Add fields or remove this empty group"
10411
- )
10412
- );
10413
- }
10414
9825
  }
10415
9826
  validateSettingsGroupFields(groupId, settingsGroup, result, allFieldIds, stats) {
10416
9827
  const groupPath = `settingsGroups.${groupId}`;
10417
- if (!Array.isArray(settingsGroup.fields)) {
10418
- return;
10419
- }
10420
9828
  stats.totalFields += settingsGroup.fields.length;
10421
- const groupFieldIds = /* @__PURE__ */ new Set();
10422
9829
  for (const [index, field] of settingsGroup.fields.entries()) {
10423
9830
  const fieldPath = `${groupPath}.fields[${index}]`;
10424
- this.validateSettingsField(
10425
- field,
10426
- fieldPath,
10427
- result,
10428
- groupFieldIds,
10429
- allFieldIds,
10430
- groupId,
10431
- stats
10432
- );
9831
+ this.validateSettingsField(field, fieldPath, result, allFieldIds, groupId, stats);
10433
9832
  }
10434
9833
  if (settingsGroup.fields.length > 15) {
10435
9834
  result.warnings.push(
@@ -10454,46 +9853,10 @@ var init_dist2 = __esm({
10454
9853
  );
10455
9854
  }
10456
9855
  }
10457
- validateSettingsField(field, fieldPath, result, groupFieldIds, allFieldIds, groupId, stats) {
10458
- if (!field.id || typeof field.id !== "string") {
10459
- result.errors.push(
10460
- this.createError(
10461
- "MISSING_FIELD_ID",
10462
- "Field must have an id property",
10463
- `${fieldPath}.id`,
10464
- "error",
10465
- "Add a unique string id for this field",
10466
- true
10467
- )
10468
- );
10469
- return;
10470
- }
10471
- if (groupFieldIds.has(field.id)) {
10472
- result.errors.push(
10473
- this.createError(
10474
- "DUPLICATE_FIELD_ID",
10475
- `Duplicate field ID '${field.id}' in settings group '${groupId}'`,
10476
- `${fieldPath}.id`,
10477
- "error",
10478
- "Use unique field IDs within each settings group",
10479
- true
10480
- )
10481
- );
9856
+ validateSettingsField(field, fieldPath, result, allFieldIds, groupId, stats) {
9857
+ if (field.type) {
9858
+ this.validateFieldTypeForSettings(field, fieldPath, result);
10482
9859
  } else {
10483
- groupFieldIds.add(field.id);
10484
- }
10485
- if (!ValidationUtils.isValidName(field.id)) {
10486
- result.warnings.push(
10487
- this.createError(
10488
- "INVALID_FIELD_ID_FORMAT",
10489
- `Field ID '${field.id}' should follow naming conventions`,
10490
- `${fieldPath}.id`,
10491
- "warning",
10492
- "Use camelCase or snake_case for field IDs"
10493
- )
10494
- );
10495
- }
10496
- if (!field.type || typeof field.type !== "string") {
10497
9860
  result.errors.push(
10498
9861
  this.createError(
10499
9862
  "MISSING_FIELD_TYPE",
@@ -10504,8 +9867,6 @@ var init_dist2 = __esm({
10504
9867
  true
10505
9868
  )
10506
9869
  );
10507
- } else {
10508
- this.validateFieldTypeForSettings(field, fieldPath, result);
10509
9870
  }
10510
9871
  if (field.localization !== void 0) {
10511
9872
  this.validateSettingsFieldLocalization(field, fieldPath, result);
@@ -10768,7 +10129,7 @@ var init_dist2 = __esm({
10768
10129
  singletonsWithSlugFields: 0,
10769
10130
  singletonsWithUniqueFields: 0
10770
10131
  };
10771
- if (!index.schema.singletonContentTypes || index.singletonContentTypes.ids.length === 0) {
10132
+ if (index.singletonContentTypes.ids.length === 0) {
10772
10133
  result.info.push(
10773
10134
  this.createError(
10774
10135
  "NO_SINGLETON_CONTENT_TYPES",
@@ -10807,18 +10168,6 @@ var init_dist2 = __esm({
10807
10168
  }
10808
10169
  validateSingletonStructure(singletonId, singletonType, result, stats) {
10809
10170
  const singletonPath = `singletonContentTypes.${singletonId}`;
10810
- if (!ValidationUtils.isValidName(singletonId)) {
10811
- result.errors.push(
10812
- this.createError(
10813
- "INVALID_SINGLETON_ID",
10814
- `Singleton content type ID '${singletonId}' is invalid`,
10815
- singletonPath,
10816
- "error",
10817
- "Use camelCase or snake_case starting with a letter",
10818
- true
10819
- )
10820
- );
10821
- }
10822
10171
  const reservedKeywords = ValidationUtils.getReservedKeywords();
10823
10172
  if (reservedKeywords.includes(singletonId.toLowerCase())) {
10824
10173
  result.errors.push(
@@ -10832,18 +10181,7 @@ var init_dist2 = __esm({
10832
10181
  )
10833
10182
  );
10834
10183
  }
10835
- if (!singletonType.displayName || typeof singletonType.displayName !== "string") {
10836
- result.errors.push(
10837
- this.createError(
10838
- "MISSING_SINGLETON_DISPLAY_NAME",
10839
- `Singleton content type '${singletonId}' must have a displayName`,
10840
- `${singletonPath}.displayName`,
10841
- "error",
10842
- "Add a descriptive displayName property",
10843
- true
10844
- )
10845
- );
10846
- } else {
10184
+ if (singletonType.displayName) {
10847
10185
  if (singletonType.displayName.length < 3) {
10848
10186
  result.warnings.push(
10849
10187
  this.createError(
@@ -10855,30 +10193,28 @@ var init_dist2 = __esm({
10855
10193
  )
10856
10194
  );
10857
10195
  }
10196
+ } else {
10197
+ result.errors.push(
10198
+ this.createError(
10199
+ "MISSING_SINGLETON_DISPLAY_NAME",
10200
+ `Singleton content type '${singletonId}' must have a displayName`,
10201
+ `${singletonPath}.displayName`,
10202
+ "error",
10203
+ "Add a descriptive displayName property",
10204
+ true
10205
+ )
10206
+ );
10858
10207
  }
10859
10208
  if (singletonType.description) {
10860
- if (typeof singletonType.description === "string") {
10861
- stats.singletonsWithDescriptions++;
10862
- if (singletonType.description.length < 10) {
10863
- result.warnings.push(
10864
- this.createError(
10865
- "SHORT_SINGLETON_DESCRIPTION",
10866
- `Singleton content type '${singletonId}' has a very short description`,
10867
- `${singletonPath}.description`,
10868
- "warning",
10869
- "Provide a more detailed description for better understanding"
10870
- )
10871
- );
10872
- }
10873
- } else {
10874
- result.errors.push(
10209
+ stats.singletonsWithDescriptions++;
10210
+ if (singletonType.description.length < 10) {
10211
+ result.warnings.push(
10875
10212
  this.createError(
10876
- "INVALID_SINGLETON_DESCRIPTION",
10877
- `Singleton content type '${singletonId}' description must be a string`,
10213
+ "SHORT_SINGLETON_DESCRIPTION",
10214
+ `Singleton content type '${singletonId}' has a very short description`,
10878
10215
  `${singletonPath}.description`,
10879
- "error",
10880
- "Use a string description",
10881
- true
10216
+ "warning",
10217
+ "Provide a more detailed description for better understanding"
10882
10218
  )
10883
10219
  );
10884
10220
  }
@@ -10906,21 +10242,7 @@ var init_dist2 = __esm({
10906
10242
  );
10907
10243
  stats.singletonsWithHierarchical++;
10908
10244
  }
10909
- const hasDirectFields = singletonType.fields && Array.isArray(singletonType.fields) && singletonType.fields.length > 0;
10910
- const hasFieldsets = singletonType.fieldsets && Array.isArray(singletonType.fieldsets) && singletonType.fieldsets.length > 0;
10911
- if (!hasDirectFields && !hasFieldsets) {
10912
- result.errors.push(
10913
- this.createError(
10914
- "SINGLETON_NO_FIELDS",
10915
- `Singleton content type '${singletonId}' has no fields or fieldsets`,
10916
- `${singletonPath}.fields`,
10917
- "error",
10918
- "Add fields or fieldsets to define the singleton's structure",
10919
- true
10920
- )
10921
- );
10922
- }
10923
- if (hasFieldsets) {
10245
+ if (singletonType.fieldsets && singletonType.fieldsets.length > 0) {
10924
10246
  stats.singletonsWithFieldsets++;
10925
10247
  }
10926
10248
  }
@@ -10928,26 +10250,9 @@ var init_dist2 = __esm({
10928
10250
  const singletonPath = `singletonContentTypes.${singletonId}`;
10929
10251
  const allFields = this.getAllFieldsFromSingleton(singletonType);
10930
10252
  stats.totalFields += allFields.length;
10931
- const fieldIds = /* @__PURE__ */ new Set();
10932
10253
  let hasLocalizationEnabled = false;
10933
10254
  for (const [index, field] of allFields.entries()) {
10934
10255
  const fieldPath = this.getFieldPath(singletonPath, field, index, singletonType);
10935
- if (field.id) {
10936
- if (fieldIds.has(field.id)) {
10937
- result.errors.push(
10938
- this.createError(
10939
- "DUPLICATE_SINGLETON_FIELD_ID",
10940
- `Duplicate field ID '${field.id}' in singleton '${singletonId}'`,
10941
- fieldPath,
10942
- "error",
10943
- "Use unique field IDs within each singleton",
10944
- true
10945
- )
10946
- );
10947
- } else {
10948
- fieldIds.add(field.id);
10949
- }
10950
- }
10951
10256
  this.validateSingletonFieldType(field, fieldPath, result, stats, singletonId);
10952
10257
  if (isFieldLocalized(field)) {
10953
10258
  hasLocalizationEnabled = true;
@@ -10993,20 +10298,6 @@ var init_dist2 = __esm({
10993
10298
  );
10994
10299
  stats.singletonsWithSlugFields++;
10995
10300
  }
10996
- if (field.type === "reference") {
10997
- const config = field.config || {};
10998
- if (config.maxItems && config.maxItems > 10) {
10999
- result.info.push(
11000
- this.createError(
11001
- "LARGE_REFERENCE_LIST_IN_SINGLETON",
11002
- `Singleton field '${field.id}' allows ${config.maxItems} references`,
11003
- fieldPath,
11004
- "info",
11005
- "Consider if a large reference list is appropriate for singleton content, or if this should be a regular content type"
11006
- )
11007
- );
11008
- }
11009
- }
11010
10301
  const problematicFieldNames = ["title", "name", "identifier", "key", "index"];
11011
10302
  if (field.id && problematicFieldNames.some((name) => field.id.toLowerCase().includes(name))) {
11012
10303
  result.info.push(
@@ -11317,18 +10608,6 @@ var init_dist2 = __esm({
11317
10608
  }
11318
10609
  validateStructure(assetTypeId, assetType, result) {
11319
10610
  const path25 = `assetTypes.${assetTypeId}`;
11320
- if (!ValidationUtils.isValidName(assetTypeId)) {
11321
- result.errors.push(
11322
- this.createError(
11323
- "INVALID_ASSET_TYPE_ID",
11324
- `Asset type ID '${assetTypeId}' is invalid`,
11325
- path25,
11326
- "error",
11327
- "Use camelCase or snake_case starting with a letter",
11328
- true
11329
- )
11330
- );
11331
- }
11332
10611
  if (ValidationUtils.getReservedKeywords().includes(assetTypeId.toLowerCase())) {
11333
10612
  result.errors.push(
11334
10613
  this.createError(
@@ -11341,7 +10620,7 @@ var init_dist2 = __esm({
11341
10620
  )
11342
10621
  );
11343
10622
  }
11344
- if (!assetType.displayName || typeof assetType.displayName !== "string") {
10623
+ if (!assetType.displayName) {
11345
10624
  result.errors.push(
11346
10625
  this.createError(
11347
10626
  "MISSING_ASSET_TYPE_DISPLAY_NAME",
@@ -11353,48 +10632,12 @@ var init_dist2 = __esm({
11353
10632
  )
11354
10633
  );
11355
10634
  }
11356
- if (assetType.displayField) {
11357
- const exists = this.getAllFields(assetType).some((f) => f.id === assetType.displayField);
11358
- if (!exists) {
11359
- result.errors.push(
11360
- this.createError(
11361
- "INVALID_ASSET_TYPE_DISPLAY_FIELD",
11362
- `Asset type '${assetTypeId}' displayField '${assetType.displayField}' does not exist`,
11363
- `${path25}.displayField`,
11364
- "error",
11365
- "Reference an existing field id",
11366
- true
11367
- )
11368
- );
11369
- }
11370
- }
11371
10635
  }
11372
10636
  validateFields(assetTypeId, assetType, result, componentIds) {
11373
10637
  const path25 = `assetTypes.${assetTypeId}`;
11374
10638
  const fields = this.getAllFields(assetType);
11375
- const fieldIds = /* @__PURE__ */ new Set();
11376
10639
  for (const field of fields) {
11377
- const fieldPath = `${path25}.fields.${field.id || "[unknown]"}`;
11378
- if (!field.id) {
11379
- result.errors.push(
11380
- this.createError("MISSING_FIELD_ID", "Field must have an id", `${fieldPath}.id`, "error")
11381
- );
11382
- continue;
11383
- }
11384
- if (fieldIds.has(field.id)) {
11385
- result.errors.push(
11386
- this.createError(
11387
- "DUPLICATE_ASSET_TYPE_FIELD_ID",
11388
- `Duplicate field ID '${field.id}' in asset type '${assetTypeId}'`,
11389
- `${fieldPath}.id`,
11390
- "error",
11391
- "Use unique field IDs within each asset type",
11392
- true
11393
- )
11394
- );
11395
- } else {
11396
- fieldIds.add(field.id);
11397
- }
10640
+ const fieldPath = `${path25}.fields.${field.id}`;
11398
10641
  if (field.type && FORBIDDEN_TYPES.has(field.type)) {
11399
10642
  result.errors.push(
11400
10643
  this.createError(
@@ -11454,33 +10697,6 @@ var init_dist2 = __esm({
11454
10697
  }
11455
10698
  validateKind(assetTypeId, assetType, result, schemaLocales, stats) {
11456
10699
  const path25 = `assetTypes.${assetTypeId}`;
11457
- const presentKinds = ASSET_KINDS.filter((k) => assetType[k] !== void 0);
11458
- if (presentKinds.length === 0) {
11459
- result.errors.push(
11460
- this.createError(
11461
- "MISSING_ASSET_KIND",
11462
- `Asset type '${assetTypeId}' must declare exactly one kind key (image, video, or file)`,
11463
- path25,
11464
- "error",
11465
- "Add one of image, video, or file",
11466
- true
11467
- )
11468
- );
11469
- return;
11470
- }
11471
- if (presentKinds.length > 1) {
11472
- result.errors.push(
11473
- this.createError(
11474
- "MULTIPLE_ASSET_KINDS",
11475
- `Asset type '${assetTypeId}' declares multiple kind keys (${presentKinds.join(", ")}). Use exactly one.`,
11476
- path25,
11477
- "error",
11478
- "Keep only one of image, video, or file",
11479
- true
11480
- )
11481
- );
11482
- return;
11483
- }
11484
10700
  const resolved = resolveAssetType(assetType);
11485
10701
  if (!resolved) return;
11486
10702
  const kind = resolved.kind;
@@ -11636,20 +10852,7 @@ var init_dist2 = __esm({
11636
10852
  validateTransformLimits(path25, transform, result) {
11637
10853
  const width = transform.width;
11638
10854
  const height = transform.height;
11639
- let format2 = transform.format;
11640
- if (format2 === "heic") {
11641
- result.errors.push(
11642
- this.createError(
11643
- "INVALID_OUTPUT_FORMAT_HEIC",
11644
- `Output format 'heic' is not supported; HEIC is a decodable input only. Use avif/webp/jpeg/png.`,
11645
- `${path25}.format`,
11646
- "error",
11647
- "Choose a web-safe output format",
11648
- true
11649
- )
11650
- );
11651
- format2 = void 0;
11652
- }
10855
+ const format2 = transform.format;
11653
10856
  const maxDim = format2 === "avif" ? CF_IMAGES_LIMITS.maxDimensionAvifPx : CF_IMAGES_LIMITS.maxDimensionPx;
11654
10857
  if (width && width > maxDim) {
11655
10858
  result.errors.push(
@@ -11689,24 +10892,8 @@ var init_dist2 = __esm({
11689
10892
  }
11690
10893
  }
11691
10894
  validateVideoKind(assetTypeId, kindPath, kindConfig, schemaLocales, result) {
11692
- const poster = kindConfig.poster;
11693
10895
  const captions = kindConfig.captions;
11694
10896
  const watermark = kindConfig.watermark;
11695
- if (poster && typeof poster.defaultTimestampPct === "number") {
11696
- const pct = poster.defaultTimestampPct;
11697
- if (pct < 0 || pct > 1) {
11698
- result.errors.push(
11699
- this.createError(
11700
- "INVALID_POSTER_TIMESTAMP",
11701
- `video.poster.defaultTimestampPct must be between 0.0 and 1.0 (got ${pct})`,
11702
- `${kindPath}.poster.defaultTimestampPct`,
11703
- "error",
11704
- "Use a value between 0.0 and 1.0",
11705
- true
11706
- )
11707
- );
11708
- }
11709
- }
11710
10897
  if (captions?.requiredLocales && Array.isArray(captions.requiredLocales)) {
11711
10898
  for (const locale of captions.requiredLocales) {
11712
10899
  if (!schemaLocales.has(locale)) {
@@ -14476,30 +13663,6 @@ var init_dist2 = __esm({
14476
13663
  );
14477
13664
  }
14478
13665
  }
14479
- if (config.maxItems !== void 0 && (!Number.isInteger(config.maxItems) || config.maxItems <= 0)) {
14480
- result.errors.push(
14481
- this.createError(
14482
- "INVALID_MAX_ITEMS",
14483
- `Field '${field.id}' has invalid maxItems: ${config.maxItems}`,
14484
- `${path25}.config.maxItems`,
14485
- "error",
14486
- "maxItems must be a positive integer",
14487
- true
14488
- )
14489
- );
14490
- }
14491
- if (config.minItems !== void 0 && config.maxItems !== void 0 && config.minItems > config.maxItems) {
14492
- result.errors.push(
14493
- this.createError(
14494
- "INVALID_ITEMS_RANGE",
14495
- `Field '${field.id}' has minItems greater than maxItems`,
14496
- `${path25}.config`,
14497
- "error",
14498
- "Ensure minItems \u2264 maxItems",
14499
- true
14500
- )
14501
- );
14502
- }
14503
13666
  }
14504
13667
  validateRichTextFieldIntegrity(field, path25, result, index) {
14505
13668
  const config = field.config || {};
@@ -14535,22 +13698,6 @@ var init_dist2 = __esm({
14535
13698
  );
14536
13699
  }
14537
13700
  }
14538
- if (config.allowedBlocks && Array.isArray(config.allowedBlocks)) {
14539
- const validBlocks = ["paragraph", "heading", "list", "quote", "code", "divider", "table"];
14540
- for (const [index2, block] of config.allowedBlocks.entries()) {
14541
- if (!validBlocks.includes(block)) {
14542
- result.warnings.push(
14543
- this.createError(
14544
- "UNKNOWN_RICH_TEXT_BLOCK",
14545
- `Rich text field '${field.id}' allows unknown block type '${block}'`,
14546
- `${path25}.config.allowedBlocks[${index2}]`,
14547
- "warning",
14548
- `Use standard block types: ${validBlocks.join(", ")}`
14549
- )
14550
- );
14551
- }
14552
- }
14553
- }
14554
13701
  }
14555
13702
  validateEnumFieldIntegrity(field, path25, result) {
14556
13703
  const config = field.config || {};
@@ -14646,35 +13793,18 @@ var init_dist2 = __esm({
14646
13793
  this.validateRequiredFieldDistribution(index, result);
14647
13794
  }
14648
13795
  validateRuleDependencies(rule, rulePath, context, result) {
14649
- const conditionsList = rule.conditions?.list ?? rule.conditions?.conditions;
14650
- if (conditionsList) {
14651
- for (const [condIndex, condition] of conditionsList.entries()) {
14652
- if (condition.field && !this.fieldExistsInContext(condition.field, context)) {
14653
- result.errors.push(
14654
- this.createError(
14655
- "CONDITION_REFERENCES_NONEXISTENT_FIELD",
14656
- `Rule condition references non-existent field '${condition.field}'`,
14657
- `${rulePath}.conditions.list[${condIndex}].field`,
14658
- "error",
14659
- "Reference only existing fields in conditions",
14660
- true
14661
- )
14662
- );
14663
- }
14664
- if (condition.field && condition.operator) {
14665
- const referencedField = this.findFieldInContext(condition.field, context);
14666
- if (referencedField && !this.isCompatibleComparator(condition.operator, referencedField.type)) {
14667
- result.warnings.push(
14668
- this.createError(
14669
- "INCOMPATIBLE_RULE_OPERATOR",
14670
- `Operator '${condition.operator}' may not be compatible with field type '${referencedField.type}'`,
14671
- `${rulePath}.conditions.list[${condIndex}].operator`,
14672
- "warning",
14673
- "Verify that the operator is appropriate for the field type"
14674
- )
14675
- );
14676
- }
14677
- }
13796
+ for (const [condIndex, condition] of rule.conditions.conditions.entries()) {
13797
+ if (condition.field && !this.fieldExistsInContext(condition.field, context)) {
13798
+ result.errors.push(
13799
+ this.createError(
13800
+ "CONDITION_REFERENCES_NONEXISTENT_FIELD",
13801
+ `Rule condition references non-existent field '${condition.field}'`,
13802
+ `${rulePath}.conditions.conditions[${condIndex}].field`,
13803
+ "error",
13804
+ "Reference only existing fields in conditions",
13805
+ true
13806
+ )
13807
+ );
14678
13808
  }
14679
13809
  }
14680
13810
  }
@@ -14866,11 +13996,11 @@ var init_dist2 = __esm({
14866
13996
  validateComponentIntegrity(index, result) {
14867
13997
  for (const [componentId, component] of index.components.entries) {
14868
13998
  const componentPath = `components.${componentId}`;
14869
- if (!component.fields || component.fields.length < 2) {
13999
+ if (component.fields.length < 2) {
14870
14000
  result.warnings.push(
14871
14001
  this.createError(
14872
14002
  "COMPONENT_TOO_SIMPLE",
14873
- `Component '${componentId}' has only ${component.fields?.length || 0} field(s)`,
14003
+ `Component '${componentId}' has only ${component.fields.length} field(s)`,
14874
14004
  componentPath,
14875
14005
  "warning",
14876
14006
  "Components should group multiple related fields to justify reusability"
@@ -15110,14 +14240,6 @@ var init_dist2 = __esm({
15110
14240
  }
15111
14241
  return null;
15112
14242
  }
15113
- isCompatibleComparator(comparator, fieldType) {
15114
- const numericComparators = ["greaterThan", "lessThan"];
15115
- const numericTypes = ["integer", "decimal"];
15116
- if (numericComparators.includes(comparator) && !numericTypes.includes(fieldType)) {
15117
- return false;
15118
- }
15119
- return true;
15120
- }
15121
14243
  getAllFields(contentType) {
15122
14244
  const fields = [];
15123
14245
  if (contentType.fields) {
@@ -15240,18 +14362,6 @@ var init_dist2 = __esm({
15240
14362
  validateRoleStructure(role, rolePath, result, stats) {
15241
14363
  if (role.baseRole) {
15242
14364
  stats.rolesWithBaseRole++;
15243
- if (!isBuiltInRole(role.baseRole)) {
15244
- result.errors.push(
15245
- this.createError(
15246
- "INVALID_BASE_ROLE",
15247
- `Role '${role.id}' has invalid baseRole '${role.baseRole}'`,
15248
- `${rolePath}.baseRole`,
15249
- "error",
15250
- `Valid base roles are: ${BUILT_IN_ROLES.join(", ")}`,
15251
- true
15252
- )
15253
- );
15254
- }
15255
14365
  }
15256
14366
  if (role.includes && role.includes.length > 0) {
15257
14367
  stats.rolesWithIncludes++;
@@ -15259,17 +14369,6 @@ var init_dist2 = __esm({
15259
14369
  if (role.permissions && role.permissions.length > 0) {
15260
14370
  stats.rolesWithExplicitPermissions++;
15261
14371
  }
15262
- if (role.displayName.length > 128) {
15263
- result.warnings.push(
15264
- this.createError(
15265
- "LONG_ROLE_DISPLAY_NAME",
15266
- `Role '${role.id}' has a very long displayName (${role.displayName.length} chars)`,
15267
- `${rolePath}.displayName`,
15268
- "warning",
15269
- "Consider using a shorter display name for better UI presentation"
15270
- )
15271
- );
15272
- }
15273
14372
  if (!role.description) {
15274
14373
  result.info.push(
15275
14374
  this.createError(
@@ -15346,21 +14445,6 @@ var init_dist2 = __esm({
15346
14445
  }
15347
14446
  for (const [permIndex, permission] of role.permissions.entries()) {
15348
14447
  const permPath = `${rolePath}.permissions[${permIndex}]`;
15349
- for (const [actionIndex, action] of permission.actions.entries()) {
15350
- if (!ROLE_ACTIONS.includes(action)) {
15351
- const suggestion = ValidationUtils.findBestMatch(action, [...ROLE_ACTIONS]);
15352
- result.errors.push(
15353
- this.createError(
15354
- "INVALID_ROLE_ACTION",
15355
- `Role '${role.id}' has invalid action '${action}'`,
15356
- `${permPath}.actions[${actionIndex}]`,
15357
- "error",
15358
- suggestion ? `Did you mean '${suggestion}'? Valid actions: ${ROLE_ACTIONS.join(", ")}` : `Valid actions are: ${ROLE_ACTIONS.join(", ")}`,
15359
- true
15360
- )
15361
- );
15362
- }
15363
- }
15364
14448
  for (const [tagIndex, tag] of permission.tags.entries()) {
15365
14449
  stats.totalAccessTagsUsed.add(tag);
15366
14450
  if (!definedAccessTags.has(tag)) {
@@ -15565,7 +14649,7 @@ var init_dist2 = __esm({
15565
14649
  }
15566
14650
  };
15567
14651
  EVOLUTION_VALIDATOR_KEY = "evolution";
15568
- CROSS_VERSION_CHECKS_SKIPPED = Object.entries(CROSS_VERSION_CHECKS).map(([code, check]) => `${check} (${code})`).join(", ");
14652
+ CROSS_VERSION_CHECKS_SKIPPED = Object.entries(CROSS_VERSION_CHECKS).map(([code, check]) => `${check.names} (${code})`).join(", ");
15569
14653
  ValidationPipeline = class {
15570
14654
  config;
15571
14655
  validators = /* @__PURE__ */ new Map();
@@ -16796,7 +15880,7 @@ async function preview(options) {
16796
15880
  return failed(extractApiError(error2, "Pre-deploy validation failed"));
16797
15881
  }
16798
15882
  const validationErrors = status.validationErrors ?? [];
16799
- const validationWarnings = status.validationWarnings ?? [];
15883
+ const validationWarnings = status.validationWarnings;
16800
15884
  const gitCommitSha = getCurrentGitCommitSha() || void 0;
16801
15885
  const previewData = {
16802
15886
  branchName,
@@ -16889,7 +15973,8 @@ async function run(options) {
16889
15973
  version: deployResult.version,
16890
15974
  durationMs: Date.now() - start,
16891
15975
  preview: previewData,
16892
- verified
15976
+ verified,
15977
+ validationWarnings: deployResult.validationWarnings
16893
15978
  };
16894
15979
  }
16895
15980
  async function verifyDeploy(branchId, projectRoot) {
@@ -18836,6 +17921,7 @@ async function runLocalVsRemote(options) {
18836
17921
  hasChanges: status.hasChanges ?? false,
18837
17922
  changes: { ...DEFAULT_CHANGES2, ...status.changes },
18838
17923
  validationErrors: status.validationErrors ?? [],
17924
+ validationWarnings: status.validationWarnings,
18839
17925
  localSchema: loaded.schema
18840
17926
  };
18841
17927
  }
@@ -22524,7 +21610,7 @@ var init_banner = __esm({
22524
21610
  init_dev_cockpit_context();
22525
21611
  init_dev_cockpit_format();
22526
21612
  init_startup_info();
22527
- CLI_VERSION2 = "0.4.0" ;
21613
+ CLI_VERSION2 = "0.6.0" ;
22528
21614
  BRAND_GLYPH = "\u25B6";
22529
21615
  BRAND_NAME = "RelyIO";
22530
21616
  LABEL_WIDTH = 9;
@@ -30679,6 +29765,13 @@ function displayDeploySuccess(version, branchName) {
30679
29765
 
30680
29766
  // src/utils/json-output.ts
30681
29767
  init_change_buckets();
29768
+ function listIssues(issues) {
29769
+ return issues.map((issue) => ({
29770
+ path: issue.path,
29771
+ message: issue.message,
29772
+ ...issue.code && { code: issue.code }
29773
+ }));
29774
+ }
30682
29775
  function listBucket(bucket) {
30683
29776
  return {
30684
29777
  added: bucket.added,
@@ -30722,25 +29815,23 @@ function createStatusJsonOutput(params) {
30722
29815
  complexity: statistics?.complexity
30723
29816
  },
30724
29817
  ...failure && { error: failure },
30725
- ...validationErrors.length > 0 && {
30726
- errors: validationErrors.map((e) => ({
30727
- path: e.path,
30728
- message: e.message,
30729
- ...e.code && { code: e.code }
30730
- }))
30731
- },
30732
- ...validationWarnings.length > 0 && {
30733
- warnings: validationWarnings.map((w) => ({
30734
- path: w.path,
30735
- message: w.message,
30736
- ...w.code && { code: w.code }
30737
- }))
30738
- },
29818
+ ...validationErrors.length > 0 && { errors: listIssues(validationErrors) },
29819
+ ...validationWarnings.length > 0 && { warnings: listIssues(validationWarnings) },
30739
29820
  ...divergence && { divergence }
30740
29821
  };
30741
29822
  }
30742
29823
  function createDeployJsonOutput(params) {
30743
- const { success: success2, branchName, branchId, version, changes, gitCommitSha, notes, error: error2 } = params;
29824
+ const {
29825
+ success: success2,
29826
+ branchName,
29827
+ branchId,
29828
+ version,
29829
+ changes,
29830
+ gitCommitSha,
29831
+ notes,
29832
+ validationWarnings,
29833
+ error: error2
29834
+ } = params;
30744
29835
  return {
30745
29836
  success: success2,
30746
29837
  ...version && { version, deployedAt: (/* @__PURE__ */ new Date()).toISOString() },
@@ -30754,6 +29845,7 @@ function createDeployJsonOutput(params) {
30754
29845
  },
30755
29846
  ...gitCommitSha && { git: { commitSha: gitCommitSha } },
30756
29847
  ...notes && { notes },
29848
+ warnings: listIssues(validationWarnings),
30757
29849
  ...error2 && { error: error2 }
30758
29850
  };
30759
29851
  }
@@ -30787,7 +29879,8 @@ function createDiffJsonOutput(params) {
30787
29879
  schemaSourcePath,
30788
29880
  hasChanges,
30789
29881
  changes,
30790
- localSchema
29882
+ localSchema,
29883
+ validationWarnings
30791
29884
  } = params;
30792
29885
  const fieldCount = (key, name) => {
30793
29886
  const section = localSchema?.[key];
@@ -30813,7 +29906,8 @@ function createDiffJsonOutput(params) {
30813
29906
  deleted: bucket.deleted,
30814
29907
  ...bucket.enabled && { enabled: bucket.enabled }
30815
29908
  })),
30816
- summary: countChanges(changes)
29909
+ summary: countChanges(changes),
29910
+ warnings: listIssues(validationWarnings)
30817
29911
  };
30818
29912
  }
30819
29913
  function countOf(count, noun) {
@@ -31265,10 +30359,16 @@ function nothingToDeploy(preview2) {
31265
30359
  branchName: preview2.branchName,
31266
30360
  branchId: preview2.branchId,
31267
30361
  changes: preview2.changes,
31268
- gitCommitSha: preview2.gitCommitSha
30362
+ gitCommitSha: preview2.gitCommitSha,
30363
+ validationWarnings: preview2.validationWarnings
31269
30364
  })
31270
30365
  );
31271
30366
  }
30367
+ var issueKey = (issue) => JSON.stringify([issue.code, issue.path, issue.message]);
30368
+ function warningsNotShown(deployed, shown) {
30369
+ const seen = new Set(shown.map((warning2) => issueKey(warning2)));
30370
+ return deployed.filter((warning2) => !seen.has(issueKey(warning2)));
30371
+ }
31272
30372
  async function handleDeploy(options) {
31273
30373
  const { json = false } = options;
31274
30374
  process.on("SIGINT", () => interrupted(json));
@@ -31379,24 +30479,26 @@ async function handleDeploy(options) {
31379
30479
  displayInfo("Your schema is already up to date with the remote branch");
31380
30480
  return;
31381
30481
  }
31382
- displaySection("Deployment Preview", "sparkles");
31383
- console.log(
31384
- THEME.primary("\u2502 ") + THEME.muted(
31385
- `Target: ${previewData.branchName} (${previewData.branchId.slice(0, 8)}...)`
31386
- )
31387
- );
31388
- console.log(
31389
- THEME.primary("\u2502 ") + THEME.muted(`Schema: ${formatSchemaSource(previewData)}`)
31390
- );
31391
- if (previewData.gitCommitSha) {
30482
+ if (!json) {
30483
+ displaySection("Deployment Preview", "sparkles");
31392
30484
  console.log(
31393
- THEME.primary("\u2502 ") + THEME.muted(`Git SHA: ${previewData.gitCommitSha.slice(0, 8)}...`)
30485
+ THEME.primary("\u2502 ") + THEME.muted(
30486
+ `Target: ${previewData.branchName} (${previewData.branchId.slice(0, 8)}...)`
30487
+ )
31394
30488
  );
31395
- }
31396
- displaySectionEnd();
31397
- displaySchemaChanges(previewData.changes);
31398
- if (previewData.validationWarnings.length > 0) {
31399
- console.log(formatValidationErrors(previewData.validationWarnings));
30489
+ console.log(
30490
+ THEME.primary("\u2502 ") + THEME.muted(`Schema: ${formatSchemaSource(previewData)}`)
30491
+ );
30492
+ if (previewData.gitCommitSha) {
30493
+ console.log(
30494
+ THEME.primary("\u2502 ") + THEME.muted(`Git SHA: ${previewData.gitCommitSha.slice(0, 8)}...`)
30495
+ );
30496
+ }
30497
+ displaySectionEnd();
30498
+ displaySchemaChanges(previewData.changes);
30499
+ if (previewData.validationWarnings.length > 0) {
30500
+ console.log(formatValidationErrors(previewData.validationWarnings));
30501
+ }
31400
30502
  }
31401
30503
  let notes;
31402
30504
  if (!json && !options.yes) {
@@ -31493,12 +30595,17 @@ async function handleDeploy(options) {
31493
30595
  version: result.version,
31494
30596
  changes: result.preview.changes,
31495
30597
  gitCommitSha: result.preview.gitCommitSha,
31496
- notes
30598
+ notes,
30599
+ validationWarnings: result.validationWarnings
31497
30600
  })
31498
30601
  );
31499
30602
  }
31500
30603
  deploySpinner?.succeed("Schema deployment completed successfully");
31501
30604
  displayDeploySuccess(result.version, result.preview.branchName);
30605
+ const unseenWarnings = warningsNotShown(result.validationWarnings, previewData.validationWarnings);
30606
+ if (unseenWarnings.length > 0) {
30607
+ console.log(formatValidationErrors(unseenWarnings));
30608
+ }
31502
30609
  console.log();
31503
30610
  switch (result.verified) {
31504
30611
  case "passed": {
@@ -32133,7 +31240,7 @@ function sandboxCommands(program2) {
32133
31240
  }
32134
31241
 
32135
31242
  // src/commands/version.ts
32136
- var CLI_VERSION = "0.4.0" ;
31243
+ var CLI_VERSION = "0.6.0" ;
32137
31244
  async function handleVersion(_options) {
32138
31245
  console.log(heading("RelyIO CLI Version Information"));
32139
31246
  const versionInfo = [
@@ -32504,7 +31611,7 @@ async function performStatusCheck(branchName, options) {
32504
31611
  hasChanges: response.hasChanges ?? false,
32505
31612
  ...response.changes ?? defaultChanges,
32506
31613
  validationErrors: response.validationErrors ?? [],
32507
- validationWarnings: response.validationWarnings ?? [],
31614
+ validationWarnings: response.validationWarnings,
32508
31615
  recommendations: response.recommendations ?? [],
32509
31616
  statistics: response.statistics,
32510
31617
  complexityAnalysis: response.complexityAnalysis
@@ -33679,19 +32786,23 @@ async function handleDiff(options) {
33679
32786
  schemaSourcePath: result.schemaSourcePath,
33680
32787
  hasChanges: result.hasChanges,
33681
32788
  changes: result.changes,
33682
- localSchema: result.localSchema
32789
+ localSchema: result.localSchema,
32790
+ validationWarnings: result.validationWarnings
33683
32791
  })
33684
32792
  );
33685
32793
  return;
33686
32794
  }
33687
32795
  displayDiffHeader(result.branchName, result.branchId);
33688
- if (!result.hasChanges) {
32796
+ if (result.hasChanges) {
32797
+ displayDiffSummary(result.changes);
32798
+ displayDetailedDiff(result.changes, result.localSchema);
32799
+ } else {
33689
32800
  console.log(THEME3.muted("No changes between local schema and remote branch"));
33690
32801
  console.log();
33691
- return;
33692
32802
  }
33693
- displayDiffSummary(result.changes);
33694
- displayDetailedDiff(result.changes, result.localSchema);
32803
+ if (result.validationWarnings.length > 0) {
32804
+ console.log(formatValidationErrors(result.validationWarnings));
32805
+ }
33695
32806
  return;
33696
32807
  }
33697
32808
  if (json) {
@@ -36403,7 +35514,7 @@ Examples:
36403
35514
  }
36404
35515
 
36405
35516
  // src/index.ts
36406
- var CLI_VERSION3 = "0.4.0" ;
35517
+ var CLI_VERSION3 = "0.6.0" ;
36407
35518
  var program = new Command();
36408
35519
  program.name("relyio").description("RelyIO CLI - Content Management, Simplified").version(CLI_VERSION3);
36409
35520
  registerGlobalOptions(program);