Developer-Dashboard

 view release on metacpan or  search on metacpan

lib/Developer/Dashboard/CLI/OpenFile.pm  view on Meta::CPAN

    my ($basename) = $match_path =~ m{([^/\\]+)$};
    $basename ||= $match_path;
    my $stem = $basename;
    $stem =~ s{\.[^.]+$}{};

    my $rank = 0;
    for my $pattern (@patterns) {
        next if !defined $pattern || $pattern eq '';
        my $regex = _compile_open_file_regex($pattern);
        my $score = 50;
        my @components = grep { defined && $_ ne '' } split m{[\\/]+}, $match_path;

        if ( $basename =~ /\A(?:$pattern)\z/i ) {
            $score = 0;
        }
        elsif ( $stem =~ /\A(?:$pattern)\z/i ) {
            $score = 1;
        }
        elsif ( $basename =~ /\A(?:$pattern)/i ) {
            $score = 2;
        }

lib/Developer/Dashboard/CLI/OpenFile.pm  view on Meta::CPAN

# _cached_archive_source_path(%args)
# Builds the stable cache location used for one extracted Java source member.
# Input: path registry object, archive file path string, and archive member path string.
# Output: extracted source file path string.
sub _cached_archive_source_path {
    my (%args) = @_;
    my $paths   = $args{paths}   || die 'Missing path registry';
    my $archive = $args{archive} || die 'Missing archive path';
    my $entry   = $args{entry}   || die 'Missing archive entry';
    my $digest  = md5_hex( join "\0", $archive, $entry );
    my @parts   = grep { defined && $_ ne '' } split m{/+}, $entry;

    return File::Spec->catfile(
        $paths->cache_root,
        'open-file',
        'java-sources',
        $digest,
        @parts,
    );
}

lib/Developer/Dashboard/SkillDispatcher.pm  view on Meta::CPAN

# Serves isolated skill browser routes and the older /skill bookmark namespace.
# Input: skill name, route path, and optional web app for rendering.
# Output: array reference HTTP response.
sub route_response {
    my ( $self, %args ) = @_;
    my $skill_name = $args{skill_name} || '';
    my $route      = defined $args{route} ? $args{route} : '';
    my @skill_layers = $self->_skill_layers($skill_name);
    return [ 404, 'text/plain; charset=utf-8', "Skill '$skill_name' not found\n" ] if !@skill_layers;

    my @parts = grep { defined && $_ ne '' } split m{/+}, $route;
    my @dashboards_roots = map { File::Spec->catdir( $_, 'dashboards' ) } @skill_layers;
    return [ 404, 'text/plain; charset=utf-8', "Skill '$skill_name' does not provide dashboards\n" ]
      if !grep { -d $_ } @dashboards_roots;

    if ( @parts && $parts[0] eq 'bookmarks' ) {
        if ( @parts == 1 ) {
            my @items = $self->_skill_bookmark_entries($skill_name);
            return [ 404, 'text/plain; charset=utf-8', "Skill '$skill_name' does not provide dashboards\n" ] if !@items;
            return [ 200, 'application/json; charset=utf-8', encode_json( { skill => $skill_name, bookmarks => \@items } ) ];
        }

lib/Developer/Dashboard/SkillDispatcher.pm  view on Meta::CPAN

    );
}

# _skill_layers($skill_name)
# Returns the participating installed roots for one skill in inheritance order.
# Input: skill repository name string and optional include_disabled flag.
# Output: ordered list of skill root directory path strings from home to leaf.
sub _skill_layers {
    my ( $self, $skill_name, %args ) = @_;
    return () if !$skill_name;
    my @segments = grep { defined && $_ ne '' } split m{/+}, $skill_name;
    return () if !@segments;
    my $root_skill = shift @segments;
    my $paths = $self->{manager}{paths};
    my @layers = $paths->can('skill_layers')
      ? $paths->skill_layers( $root_skill, %args )
      : do {
            my $skill_path = $self->{manager}->get_skill_path( $root_skill, %args ) or return ();
            ($skill_path);
        };
    return @layers if !@segments;

lib/Developer/Dashboard/SkillDispatcher.pm  view on Meta::CPAN

}

# _page_location($skill_name, $route_id)
# Resolves one skill dashboard file across every participating skill layer.
# Input: skill repository name string and route id string such as index or nav/foo.tt.
# Output: file path string and the skill layer root that provided it.
sub _page_location {
    my ( $self, $skill_name, $route_id ) = @_;
    return if !$skill_name || !$route_id;
    for my $skill_path ( $self->_skill_lookup_roots($skill_name) ) {
        my $file = File::Spec->catfile( $skill_path, 'dashboards', split m{/+}, $route_id );
        return ( $file, $skill_path ) if -f $file;
    }
    return;
}

# skill_route_spec($kind, $skill_name, $target)
# Resolves one custom config/routes.json route definition for a skill route kind.
# Input: route kind string, skill repository name string, and relative route target path.
# Output: normalized route spec hash reference or undef when no custom route exists.
sub skill_route_spec {

lib/Developer/Dashboard/SkillDispatcher.pm  view on Meta::CPAN

}

# skill_ajax_file_path($skill_name, $ajax_file)
# Resolves one layered skill-local dashboards/ajax file in deepest-first order.
# Input: skill repository name string and relative ajax file path.
# Output: absolute file path string or undef when missing.
sub skill_ajax_file_path {
    my ( $self, $skill_name, $ajax_file ) = @_;
    return if !$skill_name || !$ajax_file;
    for my $skill_path ( $self->_skill_lookup_roots($skill_name) ) {
        my $file = File::Spec->catfile( $skill_path, 'dashboards', 'ajax', split m{/+}, $ajax_file );
        return $file if -f $file;
    }
    return;
}

# skill_static_file_path($skill_name, $type, $file)
# Resolves one layered skill-local dashboards/public asset in deepest-first order.
# Input: skill repository name string, static asset type, and relative file path.
# Output: absolute file path string or undef when missing.
sub skill_static_file_path {
    my ( $self, $skill_name, $type, $file ) = @_;
    return if !$skill_name || !$type || !$file;
    for my $skill_path ( $self->_skill_lookup_roots($skill_name) ) {
        my $candidate = File::Spec->catfile( $skill_path, 'dashboards', 'public', $type, split m{/+}, $file );
        return $candidate if -f $candidate;
    }
    return;
}

# _skill_bookmark_entries($skill_name)
# Enumerates non-nav bookmark files contributed by one layered skill with deepest
# duplicates overriding shallower layers.
# Input: skill repository name string.
# Output: sorted list of bookmark entry names.

lib/Developer/Dashboard/Web/App.pm  view on Meta::CPAN


# prefixed_ajax_file_response(%args)
# Resolves one `/ajax/...` request by preferring the longest matching skill prefix
# and falling back to the normal nested saved-ajax file path when no skill-local
# handler exists.
# Input: ajax_path plus normalized request metadata.
# Output: response array reference.
sub prefixed_ajax_file_response {
    my ( $self, %args ) = @_;
    my $ajax_path = $args{ajax_path} || '';
    my @segments = grep { defined && $_ ne '' } split m{/+}, $ajax_path;
    if ( my $spec = $self->_resolve_skill_route_spec(@segments) ) {
        my $ajax_file = join '/', @{ $spec->{route_segments} || [] };
        if ( $ajax_file ne '' ) {
            my $saved_ajax_path = $self->_skill_ajax_file_path( $spec->{skill_name}, $ajax_file );
            my $route_spec = $self->_skill_ajax_route_spec( $spec->{skill_name}, $ajax_file );
            return $self->skill_ajax_file_response(
                %args,
                default_type => $route_spec ? ( $route_spec->{type} || '' ) : '',
                skill_name   => $spec->{skill_name},
                ajax_file    => $ajax_file,

lib/Developer/Dashboard/Web/App.pm  view on Meta::CPAN

# prefixed_static_file_response(%args)
# Resolves one `/js/...`, `/css/...`, or `/others/...` request by preferring the
# longest matching skill prefix and falling back to the normal nested public
# asset path when no skill-local asset exists.
# Input: asset type, requested file path, and normalized request metadata.
# Output: response array reference.
sub prefixed_static_file_response {
    my ( $self, %args ) = @_;
    my $type = $args{type} || '';
    my $file = $args{file} || '';
    my @segments = grep { defined && $_ ne '' } split m{/+}, $file;
    if ( my $spec = $self->_resolve_skill_route_spec(@segments) ) {
        my $skill_file = join '/', @{ $spec->{route_segments} || [] };
        if ( $skill_file ne '' ) {
            my $resolved = $self->_skill_static_file_path( $spec->{skill_name}, $type, $skill_file );
            my $route_spec = $self->_skill_route_spec( $type, $spec->{skill_name}, $skill_file );
            return $self->skill_static_file_response(
                %args,
                default_type => $route_spec ? ( $route_spec->{type} || '' ) : '',
                skill_name => $spec->{skill_name},
                file       => $skill_file,

lib/Developer/Dashboard/Web/App.pm  view on Meta::CPAN

    );
}

# _skill_app_fallback_response(%args)
# Attempts the /app/<skill> or /app/<skill>/<page> fallback before the blank saved-page editor opens.
# Input: unresolved app id plus normalized request metadata.
# Output: response array reference for a matched skill route, a 404 for missing namespaced skill routes, or undef when the id is not a skill route.
sub _skill_app_fallback_response {
    my ( $self, %args ) = @_;
    my $id = $args{id} || return;
    my @segments = grep { defined && $_ ne '' } split m{/+}, $id;
    return if !@segments;

    require Developer::Dashboard::SkillDispatcher;
    require Developer::Dashboard::SkillManager;
    my $dispatcher = Developer::Dashboard::SkillDispatcher->new( paths => $self->{pages} ? $self->{pages}{paths} : undef );
    my $manager = Developer::Dashboard::SkillManager->new( paths => $self->{pages} ? $self->{pages}{paths} : undef );
    my $installed_skill = $manager->get_skill_path( $segments[0], include_disabled => 1 );
    return $segments[1] ? [ 404, 'text/plain; charset=utf-8', "Not found\n" ] : undef if !$installed_skill;
    my $spec = $dispatcher->resolve_route_segments( \@segments, include_disabled => 1 );
    return $segments[1] ? [ 404, 'text/plain; charset=utf-8', "Not found\n" ] : undef if !$spec;

lib/Developer/Dashboard/Zipper.pm  view on Meta::CPAN

    return $dispatcher->skill_ajax_route_spec( $skill_name, $args{file} || '' );
}

# _url_path_escape($path)
# Escapes one slash-delimited route fragment without collapsing path separators.
# Input: relative path string.
# Output: URL-safe path string with each segment escaped independently.
sub _url_path_escape {
    my ($path) = @_;
    return '' if !defined $path || $path eq '';
    return join '/', map { uri_escape($_) } split m{/+}, $path;
}

# _saved_ajax_url_and_store(%args)
# Stores saved bookmark Ajax code under the dashboards ajax tree and returns the stable runtime URL.
# Input: runtime_root, file, type, code, and optional base_url/singleton values.
# Output: hash reference with url and file path.
sub _saved_ajax_url_and_store {
    my (%args) = @_;
    my $path = saved_ajax_file_path(%args);
    my $dir = dirname($path);

t/34-scorecard-guardrails.t  view on Meta::CPAN

sub _git_tracks {
    my ($path) = @_;
    my ( $stdout, $stderr, $exit ) = capture {
        system( 'git', '-C', $ROOT, 'ls-files', '--error-unmatch', $path );
    };
    return $exit == 0;
}

sub _slurp {
    my ($relative_path) = @_;
    my $path = File::Spec->catfile( $ROOT, split m{/}, $relative_path );
    open my $fh, '<:raw', $path or die "Unable to read $path: $!";
    local $/;
    my $text = <$fh>;
    close $fh or die "Unable to close $path: $!";
    return $text;
}

__END__

=pod

t/47-owasp-gate.t  view on Meta::CPAN

    qr/\bcolumn_info\b/,
  )
{
    unlike( $source_code, $forbidden, "repo code body avoids forbidden security pattern: $forbidden" );
}

done_testing();

sub _slurp_repo {
    my ($relative_path) = @_;
    my $path = File::Spec->catfile( $ROOT, split m{/}, $relative_path );
    open my $fh, '<:raw', $path or die "Unable to read $path: $!";
    local $/;
    my $text = <$fh>;
    close $fh or die "Unable to close $path: $!";
    return $text;
}

sub _code_only_repo_sources {
    my @paths;



( run in 0.833 second using v1.01-cache-2.11-cpan-71847e10f99 )