npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wral/studio.ui.search-filter

v0.1.0

Published

Lit web component for building Lucene search queries visually (WRAL Studio Search Filter).

Downloads

86

Readme

@wral/studio.ui.search-filter

<studio-search-filter> — a visual Lucene query builder for WRAL Studio apps (DEV-1121). Extracted from Curator's embedded query builder so any Studio app can build search-index queries with the same UI.

It edits a query string through a rule/group/if-then tree with include/exclude and all/any toggles, drag-to-reorder, and a raw "Advanced" editor. When an incoming query uses syntax the visual model cannot round-trip losslessly (unknown fields, withheld operators, mixed AND/OR precedence, + prefixes, positive implicit joins), the component automatically stays in raw mode so production queries are never silently rewritten.

Host contract

<studio-search-filter
	.value=${queryString}
	@change=${e => save(e.detail.value)}
></studio-search-filter>
  • value (String property/attribute) — the Lucene query string in.
  • change (CustomEvent, detail.value) — the edited query string out, fired on every committed edit. The event does not bubble and does not cross shadow boundaries: listen directly on the element.

The component only builds the query string. Executing searches stays in the host app (e.g. via @wral/sdk-search).

Configuration (all JSON)

The component cannot know which fields a consumer's search index allows, so the field dropdown, operator dropdowns, and boolean joiners are all supplied by the consumer as JSON — as properties or as JSON-encoded attributes. Everything defaults to the bundled publication preset (story search against api.wral.com/search), which is exactly Curator's original catalog and copy.

fields — the field catalog

{
	"tag":   { "label": "Tag", "path": "tags.name", "type": "text",
	           "hint": "e.g. UNC sports" },
	"uri":   { "label": "Article URL", "path": "uri", "type": "url" },
	"date":  { "label": "Published", "path": "metadata.articlePublishedTime",
	           "type": "date" },
	"video": { "label": "Has hero video", "path": "metadata.ogVideo",
	           "type": "video", "durationPath": "metadata.ogVideoDuration" },
	"any":   { "label": "Any field", "path": "", "type": "text" }
}
  • path is the actual search-index field name. "" marks the backend's default full-text field: bare terms (no field: prefix) parse to it and it serializes without a prefix.
  • type is one of text, url, date, number, video.
    • url gets is exactly (matches path.keyword) and is anywhere under (quoted contains on the analyzed field).
    • video models an existence flag plus a numeric companion field: has a … tests _exists_:path; the duration operators range over durationPath (required for this type).
  • hint is the value input's placeholder.
  • ops (optional) overrides the operator list for just this field.

parseLucene and the round-trip check key off the active catalog: a query naming a field outside it will not open in the visual editor.

operators — per-type operator dropdowns

{ "text": [ { "id": "phrase", "label": "contains the phrase" },
            { "id": "term", "label": "matches the word" } ] }

Consumers may subset and relabel, but not invent ids — serialization is keyed on the id. Known ids per type:

| type | ids | |--------|-----| | text | phrase, term, starts, fuzzy, exists | | url | is, under | | date | after, before, between | | number | gte, lte, between | | video | exists, longer, shorter, between |

A query using a withheld operator stays in raw mode.

booleans — the group joiners (all/any toggle)

[ { "id": "AND", "label": "all" }, { "id": "OR", "label": "any" } ]

Only AND and OR exist in the Lucene subset. Restricting to one joiner keeps queries using the other in raw mode, and withdraws the If/then action (conditionals desugar to OR-of-ANDs).

defaults — seed rules

{
	"field": "title",
	"conditional": {
		"if":   { "field": "tag", "op": "term" },
		"then": { "field": "uri", "op": "under", "negate": true },
		"else": { "field": "uri", "op": "under" }
	}
}

The field a new rule starts on, and the three seed rules of a new If/then. Falls back to the catalog's first field.

labels — copy

{ "itemNoun": "asset", "itemNounPlural": "assets" }

All copy defaults to the publication preset's story wording ("Stories must match…", "No rules yet, so every story can appear…"). itemNoun/itemNounPlural rewrite the noun-bearing strings; every individual string can also be overridden — see src/labels.mjs for the full key list.

Presets

import { publicationPreset } from '@wral/studio.ui.search-filter'{ fields, defaults, labels } for publication search. New presets (system search, DAM, Content assets) are plain data modules under src/presets/; nothing in the component needs to change to add one.

Installing

npm install @wral/studio.ui.search-filter lit
import '@wral/studio.ui.search-filter';        // registers the tag too
// or, registration only:
import '@wral/studio.ui.search-filter/define';
// model utilities for host apps:
import { equivalent, createQueryModel } from '@wral/studio.ui.search-filter';
// DOM-free (servers, workers, node tests — no lit/component):
import { equivalent } from '@wral/studio.ui.search-filter/model';

Or drop in from the CDN (lit bundled, nothing else needed):

<script type="module"
	src="https://cdn.wral.com/@wral/studio.ui.search-filter/latest/define.standalone.js"></script>

equivalent(a, b) tells hosts whether two query strings differ only by representational noise the builder normalizes (implicit vs explicit AND, NOT/-, redundant parens, …) — Curator uses it to avoid marking a collection dirty when the builder re-serializes an equivalent query.

Host expectations

  • lit is a peer dependency in the npm build (external, host-provided); only define.standalone.js bundles it.
  • The templates render studio-ui elements (studio-button, studio-button-group, studio-select, studio-input, studio-icon, studio-tooltip). They are global custom elements the host page must provide — in Studio, the shell loads https://cdn.wral.studio/ui/components/core/latest-v0/studio-ui.bundle.js. They are deliberately not imported or bundled here.
  • Theming is via CSS custom properties, every one consumed with a fallback: --studio-primary/secondary/surface/bg/fg/muted/subtle/ border/line and --wral-red/green/green-deep/purple plus --wral-font-body/ui/mono.

Developing

npm run dev    # vite demo (index.html)
npm test       # jest (ESM), jsdom for component tests
npm run lint
npm run build  # dist/index.mjs + dist/define.mjs (lit external)
               # + dist/define.standalone.js (self-contained)

Releasing

Tag vX.Y.Z and push. Bitbucket Pipelines lints/tests, builds, publishes to npm, uploads dist/ to ${S3_BUCKET}/${S3_PREFIX}/vX.Y.Z (immutable) and refreshes the vX.Y, vX and latest aliases (+ CloudFront invalidation), matching the web-components.* convention. Required repo variables: NPM_TOKEN, AWS_*, S3_BUCKET, S3_PREFIX (e.g. @wral/studio.ui.search-filter), AWS_DISTRIBUTION_ID.