Skip to content

@studnicky/drilldown ​

Deterministic multi-level grouping, faceting, and sorting for arbitrary record data, with automatic property discovery and explicit rule-driven grouping.

What it is ​

@studnicky/drilldown is a composable deterministic grouping, faceting, and sorting engine for arbitrary records. It discovers or applies rule-driven structure; it does not supply a catalogue UI or a bookstore search product.

What it is for ​

Use it when Northstar Books needs to organize title records by category, publisher, availability, or another record property and keep grouping rules explicit and testable. Consumers own records, facet presentation, and the meaning of any selected group.

Northstar Books examples ​

  • Multi-level grouping by propertyPriority maps title records to deterministic category and availability groups. It proves Northstar can choose the group order explicitly instead of hiding catalogue policy in the engine.

Install ​

bash
pnpm add @studnicky/drilldown

Requires @studnicky:registry=https://npm.pkg.github.com in .npmrc.

Overview ​

DrillDown.group builds a hierarchical grouping tree from an array of records, either from explicit GroupRuleEntity.Type rules (alphabetic, range, date, semver, CIDR, sequential, or string matchers) or from DataAnalyzer-recommended properties. FacetedDiscovery narrows a record set by concurrently-selectable facet dimensions. DrilldownRulesEntity and DrillDownConfigEntity are self-referential, schema-validated rule trees that can nest per-value grouping rules to unbounded depth.

Usage ​

Northstar's ops team wants to drill into orders by region, then category, then fulfillment status — in that exact order every time, not whatever order the engine happens to discover first. propertyPriority locks that order in: each entry listed adds one more level to the resulting tree. The example below groups a set of orders three levels deep and walks down the first branch at each level, confirming along the way that the regions split into categories and the categories split into statuses, exactly as the priority list dictates:

ts
import { OrdersFixture } from './fixtures/OrdersFixture.js';

const reusableRules: DrilldownRulesEntity.Type = {
  'group': [{ 'property': 'category', 'values': [{ 'match': 'alpha', 'type': 'string' }] }]
};
const typedRules = DrilldownRulesEntity.intake(reusableRules);

class DrilldownDemo {
  static run(): { 'categoryGroupCount': number; 'regionGroupCount': number; 'statusGroupCount': number } {
    const drillDown = new DrillDown();

    // Three levels deep: region -> category -> status
    const tree = drillDown.group(OrdersFixture.orders, {
      'minimumGroupSize': 1,
      'propertyPriority': ['region', 'category', 'status']
    });
    const firstRegion = DrilldownDemo.firstChild(tree);
    const firstCategory = DrilldownDemo.firstChild(firstRegion);

    console.log(`Root splits into ${DrilldownDemo.childCount(tree)} region groups`);
    console.log(`First region "${firstRegion?.value}" splits into ${DrilldownDemo.childCount(firstRegion)} category groups`);
    console.log(`First category "${firstCategory?.value}" splits into ${DrilldownDemo.childCount(firstCategory)} status groups`);

    return {
      'categoryGroupCount': DrilldownDemo.childCount(firstRegion),
      'regionGroupCount': DrilldownDemo.childCount(tree),
      'statusGroupCount': DrilldownDemo.childCount(firstCategory)
    };
  }

  private static childCount(node: GroupNodeInterface | undefined): number {
    const result = node?.grouped?.length ?? 0;
    return result;
  }

  private static firstChild(node: GroupNodeInterface | undefined): GroupNodeInterface | undefined {
    const result = node?.grouped?.[0] ?? undefined;
    return result;
  }
}

const results = DrilldownDemo.run();

Explicit rule types ​

Sometimes the grouping rule itself needs to be saved, shared, or validated ahead of time — not just inferred on the fly. DrilldownRulesEntity, imported from @studnicky/drilldown/entities, is the schema-backed shape for exactly that: define a rule once, like grouping by category matching the string alpha, and pass it through DrilldownRulesEntity.intake to get back a typed, validated rule tree ready to reuse across calls.

ts
const reusableRules: DrilldownRulesEntity.Type = {
  'group': [{ 'property': 'category', 'values': [{ 'match': 'alpha', 'type': 'string' }] }]
};
const typedRules = DrilldownRulesEntity.intake(reusableRules);

Try it ​

Loading example…

Live demo ​

A deterministic fixture dataset, regenerated for each demo initialization, is drilled down four levels (region → category → status → brand) to demonstrate the module's core promise: recursion bounded only by how many properties are discoverable in the data, not by a fixed depth. Click a node to expand or collapse its children.

320 synthetic orders, drilled down by region → category → status → brand
  • ▶All records320 records
    • ▶region:North America80 records
    • ▶region:Europe80 records
    • ▶region:Asia Pacific80 records
    • ▶region:Latin America80 records

Public entrypoints ​

Import pathUse it when
@studnicky/drilldown/nodeA Northstar Books server or worker needs runtime grouping and analysis.
@studnicky/drilldown/browserA browser bundle needs the same grouping primitive; it is a runtime alternative to /node.
@studnicky/drilldown/interfacesA consumer needs grouping and matcher ports as contracts.
@studnicky/drilldown/entitiesA consumer needs recursive grouping, filter, and sort schemas as contracts.
@studnicky/drilldown/typesA consumer needs exported drilldown type contracts without a runtime product.

Exports ​

SymbolPurposeImport path
DrillDownBuilds a hierarchical grouping tree from records, via explicit rules or auto-discovered properties.@studnicky/drilldown/node
DataAnalyzerDiscovers groupable properties across a dataset and recommends a grouping order.@studnicky/drilldown/node
FacetedDiscoveryNarrows a record set by concurrently-selectable, mutually-consistent facet dimensions.@studnicky/filters/facets
DrilldownRulesBuildErrorReports failure to assemble the recursive rules schema node.@studnicky/drilldown/node
ruleValidatorValidates a DrilldownRulesEntity.Type tree, reporting structural errors by path.@studnicky/drilldown/node
DrillDownConfigEntityTop-level schema-derived entity binding filter, group, and sort rules for one DrillDown.group call.@studnicky/drilldown/entities
DrilldownRulesEntitySelf-referential, schema-validated rule tree (filter/group/sort), nestable per group value to unbounded depth. Its named group-value and group-rule entities define the accepted configuration variants.@studnicky/drilldown/entities
AlphabeticRangeEntitySchema-derived alphabetic (lexicographic) range for string grouping.@studnicky/drilldown/entities
AutoGroupingConfigEntityConfiguration for automatic rule generation from discovered property values.@studnicky/drilldown/entities
CidrRangeEntitySchema-derived IPv4 CIDR block range for IP address grouping.@studnicky/drilldown/entities
DateGranularityValueEntitySchema-derived temporal granularity value (day/week/month/quarter/year).@studnicky/drilldown/entities
DateRangeEntitySchema-derived inclusive-after/exclusive-before epoch-ms date range.@studnicky/drilldown/entities
DateRangeFilterRuleEntitySchema-derived filter rule matching a property against a date range.@studnicky/drilldown/entities
DiscoverValuesOptionsEntityOptions controlling automatic value discovery during grouping.@studnicky/drilldown/entities
DiscoveryStrategyEntitySchema-derived enum of value-discovery strategies (alphabetic/distributive/quantile/sequential).@studnicky/drilldown/entities
FilterOperatorEntitySchema-derived enum of comparison operators for value filter rules.@studnicky/drilldown/entities
FilterRuleEntitySchema-derived union of date-range, numeric-range, and value filter rules.@studnicky/drilldown/entities
GranularityOptionsEntityOptions controlling automatic date-granularity selection.@studnicky/drilldown/entities
GroupingOptionsEntityOptions controlling automatic property discovery and grouping, including property exclusions.@studnicky/drilldown/entities
GroupNodeValueEntitySchema-derived value carried by a grouping tree node (the value the node's records share).@studnicky/drilldown/entities
GroupSortPropertyEntitySchema-derived enum of properties a group level can be sorted by.@studnicky/drilldown/entities
GroupValueDiscriminantEntitySchema-derived discriminant identifying which matcher/group-value variant a value is.@studnicky/drilldown/entities
JsonPropertyTypeEntitySchema-derived runtime data-type classification for a discovered property's values.@studnicky/drilldown/entities
NumericRangeFilterRuleEntitySchema-derived filter rule matching a property against a numeric range.@studnicky/drilldown/entities
OutlierMarkerEntitySchema-derived marker identifying an outlier group produced during grouping.@studnicky/drilldown/entities
PathSegmentEntitySchema-derived single segment (property + value) in a path from the tree root to a node.@studnicky/drilldown/entities
ProcessOptionsEntityOptions controlling a single DrillDown.group invocation end to end.@studnicky/drilldown/entities
PropertyBoundsEntitySchema-derived minimum/maximum bounds computed for a numeric or date property.@studnicky/drilldown/entities
PropertyOrderEntitySchema-derived ordered list of property paths for progressive multi-level grouping.@studnicky/drilldown/entities
PropertyPathEntitySchema-derived dot-delimited path identifying a discoverable property on a record.@studnicky/drilldown/entities
RangeEntitySchema-derived inclusive-minimum/exclusive-maximum numeric range.@studnicky/drilldown/entities
RangeIndicesEntitySchema-derived start/end indices identifying a numeric group's position.@studnicky/drilldown/entities
SemverRangeEntitySchema-derived semantic-version range expressed as a caret/tilde/comparator string.@studnicky/drilldown/entities
SequentialRangeEntitySchema-derived prefix/suffix/padding window for sequentially-numbered string values.@studnicky/drilldown/entities
SortDirectionEntitySchema-derived enum of sort directions (asc/desc).@studnicky/drilldown/entities
SortRuleEntitySchema-derived rule sorting group values by a named property and direction.@studnicky/drilldown/entities
ValueFilterRuleEntitySchema-derived filter rule matching a property against an explicit value list.@studnicky/drilldown/entities
DateGranularityTemporal granularity levels for grouping date values.@studnicky/drilldown/node
GroupingStrategyStrategies for partitioning numeric data into groups (distributive/quantile).@studnicky/drilldown/node
PropertyTypeEnumeration of value-shape classifications used by automatic value discovery.@studnicky/drilldown/node
DataAnalyzerInterfaceContract for discovering groupable properties and recommending a grouping order.@studnicky/drilldown/interfaces
DrillDownInterfaceContract for building a grouping tree from records and rules.@studnicky/drilldown/interfaces
MatcherHandlerInterfaceContract implemented by each matcher type: create, validate, compare, and match group values.@studnicky/drilldown/interfaces
AlphabeticMatcherInterfaceMatcher for alphabetic range membership.@studnicky/drilldown/interfaces
AnalysisResultInterfaceComplete analysis output for a dataset: discovered properties and a recommended grouping order.@studnicky/drilldown/interfaces
CidrMatcherInterfaceMatcher for IP address CIDR block membership.@studnicky/drilldown/interfaces
DateMatcherInterfaceMatcher for date/time range membership.@studnicky/drilldown/interfaces
DrillDownAnalysisInterfaceAnalysis the DrillDown engine uses to choose an automatic grouping order.@studnicky/drilldown/interfaces
GroupNodeInterfaceTree node in the hierarchical grouping structure.@studnicky/drilldown/interfaces
MatchContextInterfaceContext providing type-conversion utilities for matching operations.@studnicky/drilldown/interfaces
NodePathIndexInterfaceBidirectional index for efficient node lookup in the grouping tree.@studnicky/drilldown/interfaces
PartitionGroupInterfaceOne partitioned group of matched nodes and their shared group value.@studnicky/drilldown/interfaces
PropertyInfoInterfaceStatistical profile of a single property across all records.@studnicky/drilldown/interfaces
RangeMatcherInterfaceMatcher for numeric range membership.@studnicky/drilldown/interfaces
SemverMatcherInterfaceMatcher for semantic version constraint satisfaction.@studnicky/drilldown/interfaces
SequentialMatcherInterfaceMatcher for sequential string patterns.@studnicky/drilldown/interfaces
StringMatcherInterfaceMatcher for exact string equality.@studnicky/drilldown/interfaces
FacetAccessorMapTypeAccessor map reading one filterable string value per facet dimension from an arbitrary row shape.@studnicky/filters/facets/types
FacetFilterStateTypeCurrent faceted-drilldown selection per dimension.@studnicky/filters/facets/types
MatcherUnionTypeUnion of all matcher shapes produced by matcherRegistry.@studnicky/drilldown/types