@wordpress/abilities 0.16.0 → 0.16.1-next.v.202607070741.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.
@@ -46,7 +46,15 @@ var ajv = new import_ajv_draft_04.default({
46
46
  allowUnionTypes: true
47
47
  // Allow anyOf without explicit type
48
48
  });
49
- (0, import_ajv_formats.default)(ajv, ["date-time", "email", "hostname", "ipv4", "ipv6", "uuid"]);
49
+ (0, import_ajv_formats.default)(ajv, [
50
+ "date-time",
51
+ "email",
52
+ "hostname",
53
+ "ipv4",
54
+ "ipv6",
55
+ "uri",
56
+ "uuid"
57
+ ]);
50
58
  function formatAjvError(ajvError, param) {
51
59
  const instancePath = ajvError.instancePath ? ajvError.instancePath.replace(/\//g, "][").replace(/^\]\[/, "[") + "]" : "";
52
60
  const fullParam = param + instancePath;
@@ -72,7 +80,8 @@ function formatAjvError(ajvError, param) {
72
80
  uuid: `${fullParam} is not a valid UUID.`,
73
81
  ipv4: `${fullParam} is not a valid IP address.`,
74
82
  ipv6: `${fullParam} is not a valid IP address.`,
75
- hostname: `${fullParam} is not a valid hostname.`
83
+ hostname: `${fullParam} is not a valid hostname.`,
84
+ uri: `${fullParam} is not a valid URI.`
76
85
  };
77
86
  return formatMessages[format] || `Invalid ${format}.`;
78
87
  case "minimum":
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/validation.ts"],
4
- "sourcesContent": ["/**\n * Schema validation for client-side ability input and output schemas using AJV and ajv-formats.\n *\n * This utility provides validation for JSON Schema draft-04.\n * Rules are configured to support the intersection of common rules between JSON Schema draft-04, WordPress (a subset of JSON Schema draft-04),\n * and various providers like OpenAI and Anthropic.\n *\n * @see https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#json-schema-basics\n */\n\n/**\n * External dependencies\n */\nimport Ajv from 'ajv-draft-04';\nimport addFormats from 'ajv-formats';\n\n/**\n * Internal dependencies\n */\nimport type { ValidationError } from './types';\n\nconst ajv = new Ajv( {\n\tcoerceTypes: false, // No type coercion - AI should send proper JSON\n\tuseDefaults: true,\n\tremoveAdditional: false, // Keep additional properties\n\tallErrors: true,\n\tverbose: true,\n\tallowUnionTypes: true, // Allow anyOf without explicit type\n} );\n\naddFormats( ajv, [ 'date-time', 'email', 'hostname', 'ipv4', 'ipv6', 'uuid' ] );\n\n/**\n * Formats AJV errors into a simple error message.\n * The Abilities API will wrap this with ability_invalid_input/output to match the server side format.\n *\n * @param ajvError The AJV validation error.\n * @param param The base parameter name.\n * @return Simple error message string.\n */\nfunction formatAjvError( ajvError: any, param: string ): string {\n\t// Convert AJV's instancePath format (/0/prop) to an array like format to better match WordPress ([0][prop])\n\tconst instancePath = ajvError.instancePath\n\t\t? ajvError.instancePath.replace( /\\//g, '][' ).replace( /^\\]\\[/, '[' ) +\n\t\t ']'\n\t\t: '';\n\tconst fullParam = param + instancePath;\n\n\tswitch ( ajvError.keyword ) {\n\t\tcase 'type':\n\t\t\treturn `${ fullParam } is not of type ${ ajvError.params.type }.`;\n\n\t\tcase 'required':\n\t\t\treturn `${ ajvError.params.missingProperty } is a required property of ${ fullParam }.`;\n\n\t\tcase 'additionalProperties':\n\t\t\treturn `${ ajvError.params.additionalProperty } is not a valid property of Object.`;\n\n\t\tcase 'enum':\n\t\t\tconst enumValues = ajvError.params.allowedValues\n\t\t\t\t.map( ( v: any ) =>\n\t\t\t\t\ttypeof v === 'string' ? v : JSON.stringify( v )\n\t\t\t\t)\n\t\t\t\t.join( ', ' );\n\t\t\treturn ajvError.params.allowedValues.length === 1\n\t\t\t\t? `${ fullParam } is not ${ enumValues }.`\n\t\t\t\t: `${ fullParam } is not one of ${ enumValues }.`;\n\n\t\tcase 'pattern':\n\t\t\treturn `${ fullParam } does not match pattern ${ ajvError.params.pattern }.`;\n\n\t\tcase 'format':\n\t\t\tconst format = ajvError.params.format;\n\t\t\tconst formatMessages: Record< string, string > = {\n\t\t\t\temail: 'Invalid email address.',\n\t\t\t\t'date-time': 'Invalid date.',\n\t\t\t\tuuid: `${ fullParam } is not a valid UUID.`,\n\t\t\t\tipv4: `${ fullParam } is not a valid IP address.`,\n\t\t\t\tipv6: `${ fullParam } is not a valid IP address.`,\n\t\t\t\thostname: `${ fullParam } is not a valid hostname.`,\n\t\t\t};\n\t\t\treturn formatMessages[ format ] || `Invalid ${ format }.`;\n\n\t\tcase 'minimum':\n\t\tcase 'exclusiveMinimum':\n\t\t\treturn ajvError.keyword === 'exclusiveMinimum'\n\t\t\t\t? `${ fullParam } must be greater than ${ ajvError.params.limit }`\n\t\t\t\t: `${ fullParam } must be greater than or equal to ${ ajvError.params.limit }`;\n\n\t\tcase 'maximum':\n\t\tcase 'exclusiveMaximum':\n\t\t\treturn ajvError.keyword === 'exclusiveMaximum'\n\t\t\t\t? `${ fullParam } must be less than ${ ajvError.params.limit }`\n\t\t\t\t: `${ fullParam } must be less than or equal to ${ ajvError.params.limit }`;\n\n\t\tcase 'multipleOf':\n\t\t\treturn `${ fullParam } must be a multiple of ${ ajvError.params.multipleOf }.`;\n\n\t\tcase 'anyOf':\n\t\tcase 'oneOf':\n\t\t\treturn `${ fullParam } is invalid (failed ${ ajvError.keyword } validation).`;\n\n\t\tcase 'minLength':\n\t\t\treturn `${ fullParam } must be at least ${\n\t\t\t\tajvError.params.limit\n\t\t\t} character${ ajvError.params.limit === 1 ? '' : 's' } long.`;\n\n\t\tcase 'maxLength':\n\t\t\treturn `${ fullParam } must be at most ${\n\t\t\t\tajvError.params.limit\n\t\t\t} character${ ajvError.params.limit === 1 ? '' : 's' } long.`;\n\n\t\tcase 'minItems':\n\t\t\treturn `${ fullParam } must contain at least ${\n\t\t\t\tajvError.params.limit\n\t\t\t} item${ ajvError.params.limit === 1 ? '' : 's' }.`;\n\n\t\tcase 'maxItems':\n\t\t\treturn `${ fullParam } must contain at most ${\n\t\t\t\tajvError.params.limit\n\t\t\t} item${ ajvError.params.limit === 1 ? '' : 's' }.`;\n\n\t\tcase 'uniqueItems':\n\t\t\treturn `${ fullParam } has duplicate items.`;\n\n\t\tcase 'minProperties':\n\t\t\treturn `${ fullParam } must contain at least ${\n\t\t\t\tajvError.params.limit\n\t\t\t} propert${ ajvError.params.limit === 1 ? 'y' : 'ies' }.`;\n\n\t\tcase 'maxProperties':\n\t\t\treturn `${ fullParam } must contain at most ${\n\t\t\t\tajvError.params.limit\n\t\t\t} propert${ ajvError.params.limit === 1 ? 'y' : 'ies' }.`;\n\n\t\tdefault:\n\t\t\t// Fallback for any unhandled validation keywords\n\t\t\treturn (\n\t\t\t\tajvError.message ||\n\t\t\t\t`${ fullParam } is invalid (failed ${ ajvError.keyword } validation).`\n\t\t\t);\n\t}\n}\n\n/**\n * Validates a value against a JSON Schema.\n *\n * @param value The value to validate.\n * @param args The JSON Schema to validate against.\n * @param param Optional parameter name for error messages.\n * @return True if valid, error message string if invalid.\n */\nexport function validateValueFromSchema(\n\tvalue: any,\n\targs: Record< string, any >,\n\tparam = ''\n): true | ValidationError {\n\t// WordPress server expects schema to be an array/object\n\tif ( ! args || typeof args !== 'object' ) {\n\t\t// WordPress issues a _doing_it_wrong for invalid schema\n\t\t// Match this behavior with console.warn on client-side\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn( `Schema must be an object. Received ${ typeof args }.` );\n\t\t// Continue validation, treating as valid (matching server behavior)\n\t\treturn true;\n\t}\n\n\t// Type validation - WordPress REST API requires type to be set\n\tif ( ! args.type && ! args.anyOf && ! args.oneOf ) {\n\t\t// WordPress issues a _doing_it_wrong but continues\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn(\n\t\t\t`The \"type\" schema keyword for ${ param || 'value' } is required.`\n\t\t);\n\t\treturn true;\n\t}\n\n\ttry {\n\t\tconst { default: defaultValue, ...schemaWithoutDefault } = args;\n\t\tconst validate = ajv.compile( schemaWithoutDefault );\n\t\tconst valid = validate( value === undefined ? defaultValue : value );\n\n\t\tif ( valid ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Return the first error as a simple message string\n\t\t// The API will wrap this with ability_invalid_input/output\n\t\tif ( validate.errors && validate.errors.length > 0 ) {\n\t\t\t// For anyOf/oneOf, look for the more specific error\n\t\t\tconst anyOfError = validate.errors.find(\n\t\t\t\t( e ) => e.keyword === 'anyOf' || e.keyword === 'oneOf'\n\t\t\t);\n\t\t\tif ( anyOfError ) {\n\t\t\t\treturn formatAjvError( anyOfError, param );\n\t\t\t}\n\t\t\treturn formatAjvError( validate.errors[ 0 ], param );\n\t\t}\n\n\t\treturn `${ param } is invalid.`;\n\t} catch ( error ) {\n\t\t// Handle schema compilation errors\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( 'Schema compilation error:', error );\n\t\treturn 'Invalid schema provided for validation.';\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Schema validation for client-side ability input and output schemas using AJV and ajv-formats.\n *\n * This utility provides validation for JSON Schema draft-04.\n * Rules are configured to support the intersection of common rules between JSON Schema draft-04, WordPress (a subset of JSON Schema draft-04),\n * and various providers like OpenAI and Anthropic.\n *\n * @see https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#json-schema-basics\n */\n\n/**\n * External dependencies\n */\nimport Ajv from 'ajv-draft-04';\nimport addFormats from 'ajv-formats';\n\n/**\n * Internal dependencies\n */\nimport type { ValidationError } from './types';\n\nconst ajv = new Ajv( {\n\tcoerceTypes: false, // No type coercion - AI should send proper JSON\n\tuseDefaults: true,\n\tremoveAdditional: false, // Keep additional properties\n\tallErrors: true,\n\tverbose: true,\n\tallowUnionTypes: true, // Allow anyOf without explicit type\n} );\n\naddFormats( ajv, [\n\t'date-time',\n\t'email',\n\t'hostname',\n\t'ipv4',\n\t'ipv6',\n\t'uri',\n\t'uuid',\n] );\n\n/**\n * Formats AJV errors into a simple error message.\n * The Abilities API will wrap this with ability_invalid_input/output to match the server side format.\n *\n * @param ajvError The AJV validation error.\n * @param param The base parameter name.\n * @return Simple error message string.\n */\nfunction formatAjvError( ajvError: any, param: string ): string {\n\t// Convert AJV's instancePath format (/0/prop) to an array like format to better match WordPress ([0][prop])\n\tconst instancePath = ajvError.instancePath\n\t\t? ajvError.instancePath.replace( /\\//g, '][' ).replace( /^\\]\\[/, '[' ) +\n\t\t ']'\n\t\t: '';\n\tconst fullParam = param + instancePath;\n\n\tswitch ( ajvError.keyword ) {\n\t\tcase 'type':\n\t\t\treturn `${ fullParam } is not of type ${ ajvError.params.type }.`;\n\n\t\tcase 'required':\n\t\t\treturn `${ ajvError.params.missingProperty } is a required property of ${ fullParam }.`;\n\n\t\tcase 'additionalProperties':\n\t\t\treturn `${ ajvError.params.additionalProperty } is not a valid property of Object.`;\n\n\t\tcase 'enum':\n\t\t\tconst enumValues = ajvError.params.allowedValues\n\t\t\t\t.map( ( v: any ) =>\n\t\t\t\t\ttypeof v === 'string' ? v : JSON.stringify( v )\n\t\t\t\t)\n\t\t\t\t.join( ', ' );\n\t\t\treturn ajvError.params.allowedValues.length === 1\n\t\t\t\t? `${ fullParam } is not ${ enumValues }.`\n\t\t\t\t: `${ fullParam } is not one of ${ enumValues }.`;\n\n\t\tcase 'pattern':\n\t\t\treturn `${ fullParam } does not match pattern ${ ajvError.params.pattern }.`;\n\n\t\tcase 'format':\n\t\t\tconst format = ajvError.params.format;\n\t\t\tconst formatMessages: Record< string, string > = {\n\t\t\t\temail: 'Invalid email address.',\n\t\t\t\t'date-time': 'Invalid date.',\n\t\t\t\tuuid: `${ fullParam } is not a valid UUID.`,\n\t\t\t\tipv4: `${ fullParam } is not a valid IP address.`,\n\t\t\t\tipv6: `${ fullParam } is not a valid IP address.`,\n\t\t\t\thostname: `${ fullParam } is not a valid hostname.`,\n\t\t\t\turi: `${ fullParam } is not a valid URI.`,\n\t\t\t};\n\t\t\treturn formatMessages[ format ] || `Invalid ${ format }.`;\n\n\t\tcase 'minimum':\n\t\tcase 'exclusiveMinimum':\n\t\t\treturn ajvError.keyword === 'exclusiveMinimum'\n\t\t\t\t? `${ fullParam } must be greater than ${ ajvError.params.limit }`\n\t\t\t\t: `${ fullParam } must be greater than or equal to ${ ajvError.params.limit }`;\n\n\t\tcase 'maximum':\n\t\tcase 'exclusiveMaximum':\n\t\t\treturn ajvError.keyword === 'exclusiveMaximum'\n\t\t\t\t? `${ fullParam } must be less than ${ ajvError.params.limit }`\n\t\t\t\t: `${ fullParam } must be less than or equal to ${ ajvError.params.limit }`;\n\n\t\tcase 'multipleOf':\n\t\t\treturn `${ fullParam } must be a multiple of ${ ajvError.params.multipleOf }.`;\n\n\t\tcase 'anyOf':\n\t\tcase 'oneOf':\n\t\t\treturn `${ fullParam } is invalid (failed ${ ajvError.keyword } validation).`;\n\n\t\tcase 'minLength':\n\t\t\treturn `${ fullParam } must be at least ${\n\t\t\t\tajvError.params.limit\n\t\t\t} character${ ajvError.params.limit === 1 ? '' : 's' } long.`;\n\n\t\tcase 'maxLength':\n\t\t\treturn `${ fullParam } must be at most ${\n\t\t\t\tajvError.params.limit\n\t\t\t} character${ ajvError.params.limit === 1 ? '' : 's' } long.`;\n\n\t\tcase 'minItems':\n\t\t\treturn `${ fullParam } must contain at least ${\n\t\t\t\tajvError.params.limit\n\t\t\t} item${ ajvError.params.limit === 1 ? '' : 's' }.`;\n\n\t\tcase 'maxItems':\n\t\t\treturn `${ fullParam } must contain at most ${\n\t\t\t\tajvError.params.limit\n\t\t\t} item${ ajvError.params.limit === 1 ? '' : 's' }.`;\n\n\t\tcase 'uniqueItems':\n\t\t\treturn `${ fullParam } has duplicate items.`;\n\n\t\tcase 'minProperties':\n\t\t\treturn `${ fullParam } must contain at least ${\n\t\t\t\tajvError.params.limit\n\t\t\t} propert${ ajvError.params.limit === 1 ? 'y' : 'ies' }.`;\n\n\t\tcase 'maxProperties':\n\t\t\treturn `${ fullParam } must contain at most ${\n\t\t\t\tajvError.params.limit\n\t\t\t} propert${ ajvError.params.limit === 1 ? 'y' : 'ies' }.`;\n\n\t\tdefault:\n\t\t\t// Fallback for any unhandled validation keywords\n\t\t\treturn (\n\t\t\t\tajvError.message ||\n\t\t\t\t`${ fullParam } is invalid (failed ${ ajvError.keyword } validation).`\n\t\t\t);\n\t}\n}\n\n/**\n * Validates a value against a JSON Schema.\n *\n * @param value The value to validate.\n * @param args The JSON Schema to validate against.\n * @param param Optional parameter name for error messages.\n * @return True if valid, error message string if invalid.\n */\nexport function validateValueFromSchema(\n\tvalue: any,\n\targs: Record< string, any >,\n\tparam = ''\n): true | ValidationError {\n\t// WordPress server expects schema to be an array/object\n\tif ( ! args || typeof args !== 'object' ) {\n\t\t// WordPress issues a _doing_it_wrong for invalid schema\n\t\t// Match this behavior with console.warn on client-side\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn( `Schema must be an object. Received ${ typeof args }.` );\n\t\t// Continue validation, treating as valid (matching server behavior)\n\t\treturn true;\n\t}\n\n\t// Type validation - WordPress REST API requires type to be set\n\tif ( ! args.type && ! args.anyOf && ! args.oneOf ) {\n\t\t// WordPress issues a _doing_it_wrong but continues\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn(\n\t\t\t`The \"type\" schema keyword for ${ param || 'value' } is required.`\n\t\t);\n\t\treturn true;\n\t}\n\n\ttry {\n\t\tconst { default: defaultValue, ...schemaWithoutDefault } = args;\n\t\tconst validate = ajv.compile( schemaWithoutDefault );\n\t\tconst valid = validate( value === undefined ? defaultValue : value );\n\n\t\tif ( valid ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Return the first error as a simple message string\n\t\t// The API will wrap this with ability_invalid_input/output\n\t\tif ( validate.errors && validate.errors.length > 0 ) {\n\t\t\t// For anyOf/oneOf, look for the more specific error\n\t\t\tconst anyOfError = validate.errors.find(\n\t\t\t\t( e ) => e.keyword === 'anyOf' || e.keyword === 'oneOf'\n\t\t\t);\n\t\t\tif ( anyOfError ) {\n\t\t\t\treturn formatAjvError( anyOfError, param );\n\t\t\t}\n\t\t\treturn formatAjvError( validate.errors[ 0 ], param );\n\t\t}\n\n\t\treturn `${ param } is invalid.`;\n\t} catch ( error ) {\n\t\t// Handle schema compilation errors\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( 'Schema compilation error:', error );\n\t\treturn 'Invalid schema provided for validation.';\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["Ajv", "addFormats"]
7
7
  }
@@ -12,7 +12,15 @@ var ajv = new Ajv({
12
12
  allowUnionTypes: true
13
13
  // Allow anyOf without explicit type
14
14
  });
15
- addFormats(ajv, ["date-time", "email", "hostname", "ipv4", "ipv6", "uuid"]);
15
+ addFormats(ajv, [
16
+ "date-time",
17
+ "email",
18
+ "hostname",
19
+ "ipv4",
20
+ "ipv6",
21
+ "uri",
22
+ "uuid"
23
+ ]);
16
24
  function formatAjvError(ajvError, param) {
17
25
  const instancePath = ajvError.instancePath ? ajvError.instancePath.replace(/\//g, "][").replace(/^\]\[/, "[") + "]" : "";
18
26
  const fullParam = param + instancePath;
@@ -38,7 +46,8 @@ function formatAjvError(ajvError, param) {
38
46
  uuid: `${fullParam} is not a valid UUID.`,
39
47
  ipv4: `${fullParam} is not a valid IP address.`,
40
48
  ipv6: `${fullParam} is not a valid IP address.`,
41
- hostname: `${fullParam} is not a valid hostname.`
49
+ hostname: `${fullParam} is not a valid hostname.`,
50
+ uri: `${fullParam} is not a valid URI.`
42
51
  };
43
52
  return formatMessages[format] || `Invalid ${format}.`;
44
53
  case "minimum":
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/validation.ts"],
4
- "sourcesContent": ["/**\n * Schema validation for client-side ability input and output schemas using AJV and ajv-formats.\n *\n * This utility provides validation for JSON Schema draft-04.\n * Rules are configured to support the intersection of common rules between JSON Schema draft-04, WordPress (a subset of JSON Schema draft-04),\n * and various providers like OpenAI and Anthropic.\n *\n * @see https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#json-schema-basics\n */\n\n/**\n * External dependencies\n */\nimport Ajv from 'ajv-draft-04';\nimport addFormats from 'ajv-formats';\n\n/**\n * Internal dependencies\n */\nimport type { ValidationError } from './types';\n\nconst ajv = new Ajv( {\n\tcoerceTypes: false, // No type coercion - AI should send proper JSON\n\tuseDefaults: true,\n\tremoveAdditional: false, // Keep additional properties\n\tallErrors: true,\n\tverbose: true,\n\tallowUnionTypes: true, // Allow anyOf without explicit type\n} );\n\naddFormats( ajv, [ 'date-time', 'email', 'hostname', 'ipv4', 'ipv6', 'uuid' ] );\n\n/**\n * Formats AJV errors into a simple error message.\n * The Abilities API will wrap this with ability_invalid_input/output to match the server side format.\n *\n * @param ajvError The AJV validation error.\n * @param param The base parameter name.\n * @return Simple error message string.\n */\nfunction formatAjvError( ajvError: any, param: string ): string {\n\t// Convert AJV's instancePath format (/0/prop) to an array like format to better match WordPress ([0][prop])\n\tconst instancePath = ajvError.instancePath\n\t\t? ajvError.instancePath.replace( /\\//g, '][' ).replace( /^\\]\\[/, '[' ) +\n\t\t ']'\n\t\t: '';\n\tconst fullParam = param + instancePath;\n\n\tswitch ( ajvError.keyword ) {\n\t\tcase 'type':\n\t\t\treturn `${ fullParam } is not of type ${ ajvError.params.type }.`;\n\n\t\tcase 'required':\n\t\t\treturn `${ ajvError.params.missingProperty } is a required property of ${ fullParam }.`;\n\n\t\tcase 'additionalProperties':\n\t\t\treturn `${ ajvError.params.additionalProperty } is not a valid property of Object.`;\n\n\t\tcase 'enum':\n\t\t\tconst enumValues = ajvError.params.allowedValues\n\t\t\t\t.map( ( v: any ) =>\n\t\t\t\t\ttypeof v === 'string' ? v : JSON.stringify( v )\n\t\t\t\t)\n\t\t\t\t.join( ', ' );\n\t\t\treturn ajvError.params.allowedValues.length === 1\n\t\t\t\t? `${ fullParam } is not ${ enumValues }.`\n\t\t\t\t: `${ fullParam } is not one of ${ enumValues }.`;\n\n\t\tcase 'pattern':\n\t\t\treturn `${ fullParam } does not match pattern ${ ajvError.params.pattern }.`;\n\n\t\tcase 'format':\n\t\t\tconst format = ajvError.params.format;\n\t\t\tconst formatMessages: Record< string, string > = {\n\t\t\t\temail: 'Invalid email address.',\n\t\t\t\t'date-time': 'Invalid date.',\n\t\t\t\tuuid: `${ fullParam } is not a valid UUID.`,\n\t\t\t\tipv4: `${ fullParam } is not a valid IP address.`,\n\t\t\t\tipv6: `${ fullParam } is not a valid IP address.`,\n\t\t\t\thostname: `${ fullParam } is not a valid hostname.`,\n\t\t\t};\n\t\t\treturn formatMessages[ format ] || `Invalid ${ format }.`;\n\n\t\tcase 'minimum':\n\t\tcase 'exclusiveMinimum':\n\t\t\treturn ajvError.keyword === 'exclusiveMinimum'\n\t\t\t\t? `${ fullParam } must be greater than ${ ajvError.params.limit }`\n\t\t\t\t: `${ fullParam } must be greater than or equal to ${ ajvError.params.limit }`;\n\n\t\tcase 'maximum':\n\t\tcase 'exclusiveMaximum':\n\t\t\treturn ajvError.keyword === 'exclusiveMaximum'\n\t\t\t\t? `${ fullParam } must be less than ${ ajvError.params.limit }`\n\t\t\t\t: `${ fullParam } must be less than or equal to ${ ajvError.params.limit }`;\n\n\t\tcase 'multipleOf':\n\t\t\treturn `${ fullParam } must be a multiple of ${ ajvError.params.multipleOf }.`;\n\n\t\tcase 'anyOf':\n\t\tcase 'oneOf':\n\t\t\treturn `${ fullParam } is invalid (failed ${ ajvError.keyword } validation).`;\n\n\t\tcase 'minLength':\n\t\t\treturn `${ fullParam } must be at least ${\n\t\t\t\tajvError.params.limit\n\t\t\t} character${ ajvError.params.limit === 1 ? '' : 's' } long.`;\n\n\t\tcase 'maxLength':\n\t\t\treturn `${ fullParam } must be at most ${\n\t\t\t\tajvError.params.limit\n\t\t\t} character${ ajvError.params.limit === 1 ? '' : 's' } long.`;\n\n\t\tcase 'minItems':\n\t\t\treturn `${ fullParam } must contain at least ${\n\t\t\t\tajvError.params.limit\n\t\t\t} item${ ajvError.params.limit === 1 ? '' : 's' }.`;\n\n\t\tcase 'maxItems':\n\t\t\treturn `${ fullParam } must contain at most ${\n\t\t\t\tajvError.params.limit\n\t\t\t} item${ ajvError.params.limit === 1 ? '' : 's' }.`;\n\n\t\tcase 'uniqueItems':\n\t\t\treturn `${ fullParam } has duplicate items.`;\n\n\t\tcase 'minProperties':\n\t\t\treturn `${ fullParam } must contain at least ${\n\t\t\t\tajvError.params.limit\n\t\t\t} propert${ ajvError.params.limit === 1 ? 'y' : 'ies' }.`;\n\n\t\tcase 'maxProperties':\n\t\t\treturn `${ fullParam } must contain at most ${\n\t\t\t\tajvError.params.limit\n\t\t\t} propert${ ajvError.params.limit === 1 ? 'y' : 'ies' }.`;\n\n\t\tdefault:\n\t\t\t// Fallback for any unhandled validation keywords\n\t\t\treturn (\n\t\t\t\tajvError.message ||\n\t\t\t\t`${ fullParam } is invalid (failed ${ ajvError.keyword } validation).`\n\t\t\t);\n\t}\n}\n\n/**\n * Validates a value against a JSON Schema.\n *\n * @param value The value to validate.\n * @param args The JSON Schema to validate against.\n * @param param Optional parameter name for error messages.\n * @return True if valid, error message string if invalid.\n */\nexport function validateValueFromSchema(\n\tvalue: any,\n\targs: Record< string, any >,\n\tparam = ''\n): true | ValidationError {\n\t// WordPress server expects schema to be an array/object\n\tif ( ! args || typeof args !== 'object' ) {\n\t\t// WordPress issues a _doing_it_wrong for invalid schema\n\t\t// Match this behavior with console.warn on client-side\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn( `Schema must be an object. Received ${ typeof args }.` );\n\t\t// Continue validation, treating as valid (matching server behavior)\n\t\treturn true;\n\t}\n\n\t// Type validation - WordPress REST API requires type to be set\n\tif ( ! args.type && ! args.anyOf && ! args.oneOf ) {\n\t\t// WordPress issues a _doing_it_wrong but continues\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn(\n\t\t\t`The \"type\" schema keyword for ${ param || 'value' } is required.`\n\t\t);\n\t\treturn true;\n\t}\n\n\ttry {\n\t\tconst { default: defaultValue, ...schemaWithoutDefault } = args;\n\t\tconst validate = ajv.compile( schemaWithoutDefault );\n\t\tconst valid = validate( value === undefined ? defaultValue : value );\n\n\t\tif ( valid ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Return the first error as a simple message string\n\t\t// The API will wrap this with ability_invalid_input/output\n\t\tif ( validate.errors && validate.errors.length > 0 ) {\n\t\t\t// For anyOf/oneOf, look for the more specific error\n\t\t\tconst anyOfError = validate.errors.find(\n\t\t\t\t( e ) => e.keyword === 'anyOf' || e.keyword === 'oneOf'\n\t\t\t);\n\t\t\tif ( anyOfError ) {\n\t\t\t\treturn formatAjvError( anyOfError, param );\n\t\t\t}\n\t\t\treturn formatAjvError( validate.errors[ 0 ], param );\n\t\t}\n\n\t\treturn `${ param } is invalid.`;\n\t} catch ( error ) {\n\t\t// Handle schema compilation errors\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( 'Schema compilation error:', error );\n\t\treturn 'Invalid schema provided for validation.';\n\t}\n}\n"],
5
- "mappings": ";AAaA,OAAO,SAAS;AAChB,OAAO,gBAAgB;AAOvB,IAAM,MAAM,IAAI,IAAK;AAAA,EACpB,aAAa;AAAA;AAAA,EACb,aAAa;AAAA,EACb,kBAAkB;AAAA;AAAA,EAClB,WAAW;AAAA,EACX,SAAS;AAAA,EACT,iBAAiB;AAAA;AAClB,CAAE;AAEF,WAAY,KAAK,CAAE,aAAa,SAAS,YAAY,QAAQ,QAAQ,MAAO,CAAE;AAU9E,SAAS,eAAgB,UAAe,OAAwB;AAE/D,QAAM,eAAe,SAAS,eAC3B,SAAS,aAAa,QAAS,OAAO,IAAK,EAAE,QAAS,SAAS,GAAI,IACnE,MACA;AACH,QAAM,YAAY,QAAQ;AAE1B,UAAS,SAAS,SAAU;AAAA,IAC3B,KAAK;AACJ,aAAO,GAAI,SAAU,mBAAoB,SAAS,OAAO,IAAK;AAAA,IAE/D,KAAK;AACJ,aAAO,GAAI,SAAS,OAAO,eAAgB,8BAA+B,SAAU;AAAA,IAErF,KAAK;AACJ,aAAO,GAAI,SAAS,OAAO,kBAAmB;AAAA,IAE/C,KAAK;AACJ,YAAM,aAAa,SAAS,OAAO,cACjC;AAAA,QAAK,CAAE,MACP,OAAO,MAAM,WAAW,IAAI,KAAK,UAAW,CAAE;AAAA,MAC/C,EACC,KAAM,IAAK;AACb,aAAO,SAAS,OAAO,cAAc,WAAW,IAC7C,GAAI,SAAU,WAAY,UAAW,MACrC,GAAI,SAAU,kBAAmB,UAAW;AAAA,IAEhD,KAAK;AACJ,aAAO,GAAI,SAAU,2BAA4B,SAAS,OAAO,OAAQ;AAAA,IAE1E,KAAK;AACJ,YAAM,SAAS,SAAS,OAAO;AAC/B,YAAM,iBAA2C;AAAA,QAChD,OAAO;AAAA,QACP,aAAa;AAAA,QACb,MAAM,GAAI,SAAU;AAAA,QACpB,MAAM,GAAI,SAAU;AAAA,QACpB,MAAM,GAAI,SAAU;AAAA,QACpB,UAAU,GAAI,SAAU;AAAA,MACzB;AACA,aAAO,eAAgB,MAAO,KAAK,WAAY,MAAO;AAAA,IAEvD,KAAK;AAAA,IACL,KAAK;AACJ,aAAO,SAAS,YAAY,qBACzB,GAAI,SAAU,yBAA0B,SAAS,OAAO,KAAM,KAC9D,GAAI,SAAU,qCAAsC,SAAS,OAAO,KAAM;AAAA,IAE9E,KAAK;AAAA,IACL,KAAK;AACJ,aAAO,SAAS,YAAY,qBACzB,GAAI,SAAU,sBAAuB,SAAS,OAAO,KAAM,KAC3D,GAAI,SAAU,kCAAmC,SAAS,OAAO,KAAM;AAAA,IAE3E,KAAK;AACJ,aAAO,GAAI,SAAU,0BAA2B,SAAS,OAAO,UAAW;AAAA,IAE5E,KAAK;AAAA,IACL,KAAK;AACJ,aAAO,GAAI,SAAU,uBAAwB,SAAS,OAAQ;AAAA,IAE/D,KAAK;AACJ,aAAO,GAAI,SAAU,qBACpB,SAAS,OAAO,KACjB,aAAc,SAAS,OAAO,UAAU,IAAI,KAAK,GAAI;AAAA,IAEtD,KAAK;AACJ,aAAO,GAAI,SAAU,oBACpB,SAAS,OAAO,KACjB,aAAc,SAAS,OAAO,UAAU,IAAI,KAAK,GAAI;AAAA,IAEtD,KAAK;AACJ,aAAO,GAAI,SAAU,0BACpB,SAAS,OAAO,KACjB,QAAS,SAAS,OAAO,UAAU,IAAI,KAAK,GAAI;AAAA,IAEjD,KAAK;AACJ,aAAO,GAAI,SAAU,yBACpB,SAAS,OAAO,KACjB,QAAS,SAAS,OAAO,UAAU,IAAI,KAAK,GAAI;AAAA,IAEjD,KAAK;AACJ,aAAO,GAAI,SAAU;AAAA,IAEtB,KAAK;AACJ,aAAO,GAAI,SAAU,0BACpB,SAAS,OAAO,KACjB,WAAY,SAAS,OAAO,UAAU,IAAI,MAAM,KAAM;AAAA,IAEvD,KAAK;AACJ,aAAO,GAAI,SAAU,yBACpB,SAAS,OAAO,KACjB,WAAY,SAAS,OAAO,UAAU,IAAI,MAAM,KAAM;AAAA,IAEvD;AAEC,aACC,SAAS,WACT,GAAI,SAAU,uBAAwB,SAAS,OAAQ;AAAA,EAE1D;AACD;AAUO,SAAS,wBACf,OACA,MACA,QAAQ,IACiB;AAEzB,MAAK,CAAE,QAAQ,OAAO,SAAS,UAAW;AAIzC,YAAQ,KAAM,sCAAuC,OAAO,IAAK,GAAI;AAErE,WAAO;AAAA,EACR;AAGA,MAAK,CAAE,KAAK,QAAQ,CAAE,KAAK,SAAS,CAAE,KAAK,OAAQ;AAGlD,YAAQ;AAAA,MACP,iCAAkC,SAAS,OAAQ;AAAA,IACpD;AACA,WAAO;AAAA,EACR;AAEA,MAAI;AACH,UAAM,EAAE,SAAS,cAAc,GAAG,qBAAqB,IAAI;AAC3D,UAAM,WAAW,IAAI,QAAS,oBAAqB;AACnD,UAAM,QAAQ,SAAU,UAAU,SAAY,eAAe,KAAM;AAEnE,QAAK,OAAQ;AACZ,aAAO;AAAA,IACR;AAIA,QAAK,SAAS,UAAU,SAAS,OAAO,SAAS,GAAI;AAEpD,YAAM,aAAa,SAAS,OAAO;AAAA,QAClC,CAAE,MAAO,EAAE,YAAY,WAAW,EAAE,YAAY;AAAA,MACjD;AACA,UAAK,YAAa;AACjB,eAAO,eAAgB,YAAY,KAAM;AAAA,MAC1C;AACA,aAAO,eAAgB,SAAS,OAAQ,CAAE,GAAG,KAAM;AAAA,IACpD;AAEA,WAAO,GAAI,KAAM;AAAA,EAClB,SAAU,OAAQ;AAGjB,YAAQ,MAAO,6BAA6B,KAAM;AAClD,WAAO;AAAA,EACR;AACD;",
4
+ "sourcesContent": ["/**\n * Schema validation for client-side ability input and output schemas using AJV and ajv-formats.\n *\n * This utility provides validation for JSON Schema draft-04.\n * Rules are configured to support the intersection of common rules between JSON Schema draft-04, WordPress (a subset of JSON Schema draft-04),\n * and various providers like OpenAI and Anthropic.\n *\n * @see https://developer.wordpress.org/rest-api/extending-the-rest-api/schema/#json-schema-basics\n */\n\n/**\n * External dependencies\n */\nimport Ajv from 'ajv-draft-04';\nimport addFormats from 'ajv-formats';\n\n/**\n * Internal dependencies\n */\nimport type { ValidationError } from './types';\n\nconst ajv = new Ajv( {\n\tcoerceTypes: false, // No type coercion - AI should send proper JSON\n\tuseDefaults: true,\n\tremoveAdditional: false, // Keep additional properties\n\tallErrors: true,\n\tverbose: true,\n\tallowUnionTypes: true, // Allow anyOf without explicit type\n} );\n\naddFormats( ajv, [\n\t'date-time',\n\t'email',\n\t'hostname',\n\t'ipv4',\n\t'ipv6',\n\t'uri',\n\t'uuid',\n] );\n\n/**\n * Formats AJV errors into a simple error message.\n * The Abilities API will wrap this with ability_invalid_input/output to match the server side format.\n *\n * @param ajvError The AJV validation error.\n * @param param The base parameter name.\n * @return Simple error message string.\n */\nfunction formatAjvError( ajvError: any, param: string ): string {\n\t// Convert AJV's instancePath format (/0/prop) to an array like format to better match WordPress ([0][prop])\n\tconst instancePath = ajvError.instancePath\n\t\t? ajvError.instancePath.replace( /\\//g, '][' ).replace( /^\\]\\[/, '[' ) +\n\t\t ']'\n\t\t: '';\n\tconst fullParam = param + instancePath;\n\n\tswitch ( ajvError.keyword ) {\n\t\tcase 'type':\n\t\t\treturn `${ fullParam } is not of type ${ ajvError.params.type }.`;\n\n\t\tcase 'required':\n\t\t\treturn `${ ajvError.params.missingProperty } is a required property of ${ fullParam }.`;\n\n\t\tcase 'additionalProperties':\n\t\t\treturn `${ ajvError.params.additionalProperty } is not a valid property of Object.`;\n\n\t\tcase 'enum':\n\t\t\tconst enumValues = ajvError.params.allowedValues\n\t\t\t\t.map( ( v: any ) =>\n\t\t\t\t\ttypeof v === 'string' ? v : JSON.stringify( v )\n\t\t\t\t)\n\t\t\t\t.join( ', ' );\n\t\t\treturn ajvError.params.allowedValues.length === 1\n\t\t\t\t? `${ fullParam } is not ${ enumValues }.`\n\t\t\t\t: `${ fullParam } is not one of ${ enumValues }.`;\n\n\t\tcase 'pattern':\n\t\t\treturn `${ fullParam } does not match pattern ${ ajvError.params.pattern }.`;\n\n\t\tcase 'format':\n\t\t\tconst format = ajvError.params.format;\n\t\t\tconst formatMessages: Record< string, string > = {\n\t\t\t\temail: 'Invalid email address.',\n\t\t\t\t'date-time': 'Invalid date.',\n\t\t\t\tuuid: `${ fullParam } is not a valid UUID.`,\n\t\t\t\tipv4: `${ fullParam } is not a valid IP address.`,\n\t\t\t\tipv6: `${ fullParam } is not a valid IP address.`,\n\t\t\t\thostname: `${ fullParam } is not a valid hostname.`,\n\t\t\t\turi: `${ fullParam } is not a valid URI.`,\n\t\t\t};\n\t\t\treturn formatMessages[ format ] || `Invalid ${ format }.`;\n\n\t\tcase 'minimum':\n\t\tcase 'exclusiveMinimum':\n\t\t\treturn ajvError.keyword === 'exclusiveMinimum'\n\t\t\t\t? `${ fullParam } must be greater than ${ ajvError.params.limit }`\n\t\t\t\t: `${ fullParam } must be greater than or equal to ${ ajvError.params.limit }`;\n\n\t\tcase 'maximum':\n\t\tcase 'exclusiveMaximum':\n\t\t\treturn ajvError.keyword === 'exclusiveMaximum'\n\t\t\t\t? `${ fullParam } must be less than ${ ajvError.params.limit }`\n\t\t\t\t: `${ fullParam } must be less than or equal to ${ ajvError.params.limit }`;\n\n\t\tcase 'multipleOf':\n\t\t\treturn `${ fullParam } must be a multiple of ${ ajvError.params.multipleOf }.`;\n\n\t\tcase 'anyOf':\n\t\tcase 'oneOf':\n\t\t\treturn `${ fullParam } is invalid (failed ${ ajvError.keyword } validation).`;\n\n\t\tcase 'minLength':\n\t\t\treturn `${ fullParam } must be at least ${\n\t\t\t\tajvError.params.limit\n\t\t\t} character${ ajvError.params.limit === 1 ? '' : 's' } long.`;\n\n\t\tcase 'maxLength':\n\t\t\treturn `${ fullParam } must be at most ${\n\t\t\t\tajvError.params.limit\n\t\t\t} character${ ajvError.params.limit === 1 ? '' : 's' } long.`;\n\n\t\tcase 'minItems':\n\t\t\treturn `${ fullParam } must contain at least ${\n\t\t\t\tajvError.params.limit\n\t\t\t} item${ ajvError.params.limit === 1 ? '' : 's' }.`;\n\n\t\tcase 'maxItems':\n\t\t\treturn `${ fullParam } must contain at most ${\n\t\t\t\tajvError.params.limit\n\t\t\t} item${ ajvError.params.limit === 1 ? '' : 's' }.`;\n\n\t\tcase 'uniqueItems':\n\t\t\treturn `${ fullParam } has duplicate items.`;\n\n\t\tcase 'minProperties':\n\t\t\treturn `${ fullParam } must contain at least ${\n\t\t\t\tajvError.params.limit\n\t\t\t} propert${ ajvError.params.limit === 1 ? 'y' : 'ies' }.`;\n\n\t\tcase 'maxProperties':\n\t\t\treturn `${ fullParam } must contain at most ${\n\t\t\t\tajvError.params.limit\n\t\t\t} propert${ ajvError.params.limit === 1 ? 'y' : 'ies' }.`;\n\n\t\tdefault:\n\t\t\t// Fallback for any unhandled validation keywords\n\t\t\treturn (\n\t\t\t\tajvError.message ||\n\t\t\t\t`${ fullParam } is invalid (failed ${ ajvError.keyword } validation).`\n\t\t\t);\n\t}\n}\n\n/**\n * Validates a value against a JSON Schema.\n *\n * @param value The value to validate.\n * @param args The JSON Schema to validate against.\n * @param param Optional parameter name for error messages.\n * @return True if valid, error message string if invalid.\n */\nexport function validateValueFromSchema(\n\tvalue: any,\n\targs: Record< string, any >,\n\tparam = ''\n): true | ValidationError {\n\t// WordPress server expects schema to be an array/object\n\tif ( ! args || typeof args !== 'object' ) {\n\t\t// WordPress issues a _doing_it_wrong for invalid schema\n\t\t// Match this behavior with console.warn on client-side\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn( `Schema must be an object. Received ${ typeof args }.` );\n\t\t// Continue validation, treating as valid (matching server behavior)\n\t\treturn true;\n\t}\n\n\t// Type validation - WordPress REST API requires type to be set\n\tif ( ! args.type && ! args.anyOf && ! args.oneOf ) {\n\t\t// WordPress issues a _doing_it_wrong but continues\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.warn(\n\t\t\t`The \"type\" schema keyword for ${ param || 'value' } is required.`\n\t\t);\n\t\treturn true;\n\t}\n\n\ttry {\n\t\tconst { default: defaultValue, ...schemaWithoutDefault } = args;\n\t\tconst validate = ajv.compile( schemaWithoutDefault );\n\t\tconst valid = validate( value === undefined ? defaultValue : value );\n\n\t\tif ( valid ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Return the first error as a simple message string\n\t\t// The API will wrap this with ability_invalid_input/output\n\t\tif ( validate.errors && validate.errors.length > 0 ) {\n\t\t\t// For anyOf/oneOf, look for the more specific error\n\t\t\tconst anyOfError = validate.errors.find(\n\t\t\t\t( e ) => e.keyword === 'anyOf' || e.keyword === 'oneOf'\n\t\t\t);\n\t\t\tif ( anyOfError ) {\n\t\t\t\treturn formatAjvError( anyOfError, param );\n\t\t\t}\n\t\t\treturn formatAjvError( validate.errors[ 0 ], param );\n\t\t}\n\n\t\treturn `${ param } is invalid.`;\n\t} catch ( error ) {\n\t\t// Handle schema compilation errors\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.error( 'Schema compilation error:', error );\n\t\treturn 'Invalid schema provided for validation.';\n\t}\n}\n"],
5
+ "mappings": ";AAaA,OAAO,SAAS;AAChB,OAAO,gBAAgB;AAOvB,IAAM,MAAM,IAAI,IAAK;AAAA,EACpB,aAAa;AAAA;AAAA,EACb,aAAa;AAAA,EACb,kBAAkB;AAAA;AAAA,EAClB,WAAW;AAAA,EACX,SAAS;AAAA,EACT,iBAAiB;AAAA;AAClB,CAAE;AAEF,WAAY,KAAK;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAE;AAUF,SAAS,eAAgB,UAAe,OAAwB;AAE/D,QAAM,eAAe,SAAS,eAC3B,SAAS,aAAa,QAAS,OAAO,IAAK,EAAE,QAAS,SAAS,GAAI,IACnE,MACA;AACH,QAAM,YAAY,QAAQ;AAE1B,UAAS,SAAS,SAAU;AAAA,IAC3B,KAAK;AACJ,aAAO,GAAI,SAAU,mBAAoB,SAAS,OAAO,IAAK;AAAA,IAE/D,KAAK;AACJ,aAAO,GAAI,SAAS,OAAO,eAAgB,8BAA+B,SAAU;AAAA,IAErF,KAAK;AACJ,aAAO,GAAI,SAAS,OAAO,kBAAmB;AAAA,IAE/C,KAAK;AACJ,YAAM,aAAa,SAAS,OAAO,cACjC;AAAA,QAAK,CAAE,MACP,OAAO,MAAM,WAAW,IAAI,KAAK,UAAW,CAAE;AAAA,MAC/C,EACC,KAAM,IAAK;AACb,aAAO,SAAS,OAAO,cAAc,WAAW,IAC7C,GAAI,SAAU,WAAY,UAAW,MACrC,GAAI,SAAU,kBAAmB,UAAW;AAAA,IAEhD,KAAK;AACJ,aAAO,GAAI,SAAU,2BAA4B,SAAS,OAAO,OAAQ;AAAA,IAE1E,KAAK;AACJ,YAAM,SAAS,SAAS,OAAO;AAC/B,YAAM,iBAA2C;AAAA,QAChD,OAAO;AAAA,QACP,aAAa;AAAA,QACb,MAAM,GAAI,SAAU;AAAA,QACpB,MAAM,GAAI,SAAU;AAAA,QACpB,MAAM,GAAI,SAAU;AAAA,QACpB,UAAU,GAAI,SAAU;AAAA,QACxB,KAAK,GAAI,SAAU;AAAA,MACpB;AACA,aAAO,eAAgB,MAAO,KAAK,WAAY,MAAO;AAAA,IAEvD,KAAK;AAAA,IACL,KAAK;AACJ,aAAO,SAAS,YAAY,qBACzB,GAAI,SAAU,yBAA0B,SAAS,OAAO,KAAM,KAC9D,GAAI,SAAU,qCAAsC,SAAS,OAAO,KAAM;AAAA,IAE9E,KAAK;AAAA,IACL,KAAK;AACJ,aAAO,SAAS,YAAY,qBACzB,GAAI,SAAU,sBAAuB,SAAS,OAAO,KAAM,KAC3D,GAAI,SAAU,kCAAmC,SAAS,OAAO,KAAM;AAAA,IAE3E,KAAK;AACJ,aAAO,GAAI,SAAU,0BAA2B,SAAS,OAAO,UAAW;AAAA,IAE5E,KAAK;AAAA,IACL,KAAK;AACJ,aAAO,GAAI,SAAU,uBAAwB,SAAS,OAAQ;AAAA,IAE/D,KAAK;AACJ,aAAO,GAAI,SAAU,qBACpB,SAAS,OAAO,KACjB,aAAc,SAAS,OAAO,UAAU,IAAI,KAAK,GAAI;AAAA,IAEtD,KAAK;AACJ,aAAO,GAAI,SAAU,oBACpB,SAAS,OAAO,KACjB,aAAc,SAAS,OAAO,UAAU,IAAI,KAAK,GAAI;AAAA,IAEtD,KAAK;AACJ,aAAO,GAAI,SAAU,0BACpB,SAAS,OAAO,KACjB,QAAS,SAAS,OAAO,UAAU,IAAI,KAAK,GAAI;AAAA,IAEjD,KAAK;AACJ,aAAO,GAAI,SAAU,yBACpB,SAAS,OAAO,KACjB,QAAS,SAAS,OAAO,UAAU,IAAI,KAAK,GAAI;AAAA,IAEjD,KAAK;AACJ,aAAO,GAAI,SAAU;AAAA,IAEtB,KAAK;AACJ,aAAO,GAAI,SAAU,0BACpB,SAAS,OAAO,KACjB,WAAY,SAAS,OAAO,UAAU,IAAI,MAAM,KAAM;AAAA,IAEvD,KAAK;AACJ,aAAO,GAAI,SAAU,yBACpB,SAAS,OAAO,KACjB,WAAY,SAAS,OAAO,UAAU,IAAI,MAAM,KAAM;AAAA,IAEvD;AAEC,aACC,SAAS,WACT,GAAI,SAAU,uBAAwB,SAAS,OAAQ;AAAA,EAE1D;AACD;AAUO,SAAS,wBACf,OACA,MACA,QAAQ,IACiB;AAEzB,MAAK,CAAE,QAAQ,OAAO,SAAS,UAAW;AAIzC,YAAQ,KAAM,sCAAuC,OAAO,IAAK,GAAI;AAErE,WAAO;AAAA,EACR;AAGA,MAAK,CAAE,KAAK,QAAQ,CAAE,KAAK,SAAS,CAAE,KAAK,OAAQ;AAGlD,YAAQ;AAAA,MACP,iCAAkC,SAAS,OAAQ;AAAA,IACpD;AACA,WAAO;AAAA,EACR;AAEA,MAAI;AACH,UAAM,EAAE,SAAS,cAAc,GAAG,qBAAqB,IAAI;AAC3D,UAAM,WAAW,IAAI,QAAS,oBAAqB;AACnD,UAAM,QAAQ,SAAU,UAAU,SAAY,eAAe,KAAM;AAEnE,QAAK,OAAQ;AACZ,aAAO;AAAA,IACR;AAIA,QAAK,SAAS,UAAU,SAAS,OAAO,SAAS,GAAI;AAEpD,YAAM,aAAa,SAAS,OAAO;AAAA,QAClC,CAAE,MAAO,EAAE,YAAY,WAAW,EAAE,YAAY;AAAA,MACjD;AACA,UAAK,YAAa;AACjB,eAAO,eAAgB,YAAY,KAAM;AAAA,MAC1C;AACA,aAAO,eAAgB,SAAS,OAAQ,CAAE,GAAG,KAAM;AAAA,IACpD;AAEA,WAAO,GAAI,KAAM;AAAA,EAClB,SAAU,OAAQ;AAGjB,YAAQ,MAAO,6BAA6B,KAAM;AAClD,WAAO;AAAA,EACR;AACD;",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;GAEG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA6H/C;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,GAAG,EACV,IAAI,EAAE,MAAM,CAAE,MAAM,EAAE,GAAG,CAAE,EAC3B,KAAK,SAAK,GACR,IAAI,GAAG,eAAe,CAkDxB"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;GAEG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAsI/C;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,GAAG,EACV,IAAI,EAAE,MAAM,CAAE,MAAM,EAAE,GAAG,CAAE,EAC3B,KAAK,SAAK,GACR,IAAI,GAAG,eAAe,CAkDxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/abilities",
3
- "version": "0.16.0",
3
+ "version": "0.16.1-next.v.202607070741.0+a51d59513",
4
4
  "description": "JavaScript client for WordPress Abilities API.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -45,8 +45,8 @@
45
45
  },
46
46
  "types": "build-types",
47
47
  "dependencies": {
48
- "@wordpress/data": "^10.50.0",
49
- "@wordpress/i18n": "^6.23.0",
48
+ "@wordpress/data": "^10.50.1-next.v.202607070741.0+a51d59513",
49
+ "@wordpress/i18n": "^6.23.1-next.v.202607070741.0+a51d59513",
50
50
  "ajv": "^8.17.1",
51
51
  "ajv-draft-04": "^1.0.0",
52
52
  "ajv-formats": "^3.0.1"
@@ -54,5 +54,5 @@
54
54
  "publishConfig": {
55
55
  "access": "public"
56
56
  },
57
- "gitHead": "fee6e24e8e63d36f6bbcf487c193461e9bd79753"
57
+ "gitHead": "f637726e370c8b23695ed9af82adbe171ad235d8"
58
58
  }
@@ -249,6 +249,16 @@ describe( 'validateValueFromSchema', () => {
249
249
  ' is not a valid hostname.'
250
250
  );
251
251
  } );
252
+
253
+ it( 'should validate URI format', () => {
254
+ const schema = { type: 'string', format: 'uri' };
255
+ expect(
256
+ validateValueFromSchema( 'https://example.com/path', schema )
257
+ ).toBe( true );
258
+ expect( validateValueFromSchema( 'not a uri', schema ) ).toBe(
259
+ ' is not a valid URI.'
260
+ );
261
+ } );
252
262
  } );
253
263
 
254
264
  describe( 'pattern validation', () => {
package/src/validation.ts CHANGED
@@ -28,7 +28,15 @@ const ajv = new Ajv( {
28
28
  allowUnionTypes: true, // Allow anyOf without explicit type
29
29
  } );
30
30
 
31
- addFormats( ajv, [ 'date-time', 'email', 'hostname', 'ipv4', 'ipv6', 'uuid' ] );
31
+ addFormats( ajv, [
32
+ 'date-time',
33
+ 'email',
34
+ 'hostname',
35
+ 'ipv4',
36
+ 'ipv6',
37
+ 'uri',
38
+ 'uuid',
39
+ ] );
32
40
 
33
41
  /**
34
42
  * Formats AJV errors into a simple error message.
@@ -78,6 +86,7 @@ function formatAjvError( ajvError: any, param: string ): string {
78
86
  ipv4: `${ fullParam } is not a valid IP address.`,
79
87
  ipv6: `${ fullParam } is not a valid IP address.`,
80
88
  hostname: `${ fullParam } is not a valid hostname.`,
89
+ uri: `${ fullParam } is not a valid URI.`,
81
90
  };
82
91
  return formatMessages[ format ] || `Invalid ${ format }.`;
83
92