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

@clxmedia/cliq-flow

v1.0.11

Published

CLiQ Flow Engine

Readme

Usage

FlowEngine is a lightweight, data‑only workflow helper. It takes:

  1. A "template" (graph definition) describing nodes and their downstream relationships.
  2. A collection of runtime "items" (any shape with at least a slug string) representing the concrete work objects.

From those it can:

  • Build a full flow instance (itemsToFlowInstance) with status for every template node (real or mocked if missing).
  • Determine which items are now activatable (getItemsToActivate).
  • Determine which completed items must be reset when restarting (getItemsToReset).
  • Tell if the flow can be reset from the perspective of a given task slug (canResetFlow).

Item Agnostic by Design

FlowEngine<TItem> is generic. It does not care if your items are CLX tasks, tickets, jobs, forms, or custom domain objects—so long as each item exposes a slug (used to line up with the template).

Status detection is the only behavioral coupling. You supply (or let the engine auto‑discover) a detectStatus(item) -> 'pending' | 'open' | 'closed' function. If you omit one, the engine runs a small predicate list (FlowItemStatusDetectorMappings) to guess the status; today it ships with a legacy CLXTask detector keyed off is_active / closed_at.

If none match, status defaults to pending.

Core Concepts

| Concept | Summary | | ------- | ------- | | Flow Template | Static directed graph keyed by internal node ids. Each node holds a slug, list of downstream node ids, and optional joinCondition (AND or OR). | | Node Slug | The external business identifier; must match the slug on your runtime items. | | Join Condition | Governs activation: AND = all predecessors must be closed; OR = any predecessor closed. Missing value defaults to AND. | | Restart Node | Special node id (restartNodeId) that acts as the anchor for reset operations. | | Flow Instance | Runtime projection created from template + items (real + any mock placeholders for missing slugs). |

Status Semantics

pending -> Not yet active (eligible for activation once dependencies satisfied). open -> In progress (active but not yet completed). closed -> Completed / finished.

You control the mapping by providing an explicit detector:

const engine = new FlowEngine<MyItem>(template, item => {
	if (item.doneAt) return 'closed';
	if (item.startedAt) return 'open';
	return 'pending';
});

Or rely on built‑ins (for legacy CLXTask shape):

const engine = new FlowEngine<CLXTask>(template); // auto-detects via is_active / closed_at

Defining a Flow Template

import { FlowEngine } from 'cliq-flow';
import type { FlowTemplate } from 'cliq-flow/types';

const onboardingTemplate: FlowTemplate = {
	nodes: {
		share: { slug: 'share_onboarding_form', downstream: ['grant'] },
		grant: { slug: 'grant_access_gbp_manager', downstream: ['create'] },
		create: { slug: 'create_gbpa_ads', downstream: ['review'] },
		review: { slug: 'review_gbpa_ads', downstream: ['launch'] },
		launch: { slug: 'launch_gbpa_ads', downstream: [] },
	},
	startNodes: ['share'],
	restartNodeId: 'share',
	restartNodeLabel: 'Restart Onboarding',
};

Node ids (share, grant, etc.) are internal; slugs connect to item data.

Using with CLXTask (Example Only)

import { FlowEngine } from 'cliq-flow';
import { cliqTaskLibrary } from './fixtures/tasks'; // sample array of CLXTask

const engine = new FlowEngine(onboardingTemplate); // auto status detection

const activatable = engine.getItemsToActivate(cliqTaskLibrary);
// -> tasks currently in 'pending' whose predecessors are satisfied

const flowInstance = engine.itemsToFlowInstance(cliqTaskLibrary);
// flowInstance.nodes[nodeId].status holds evaluated status

const canReset = engine.canResetFlow('create_gbpa_ads');
// { canReset: true | false, resetLabel?: string }

const toReset = engine.getItemsToReset(cliqTaskLibrary);
// -> closed successor tasks of the restart node

Using with ANY Other Item Type

Provide a detector if your shape differs:

interface Job { slug: string; started?: string; finished?: string; }

const jobDetector = (job: Job) => job.finished ? 'closed' : job.started ? 'open' : 'pending';

```ts
const jobEngine = new FlowEngine<Job>(onboardingTemplate, jobDetector);

That is enough; the engine only reads slug and the detector result.

FlowEngineWrapper

FlowEngineWrapper is a higher-level abstraction that manages multiple FlowEngine instances, each corresponding to a different flow template. It allows you to work with a collection of templates and perform operations across all of them or target a specific template by its GUID.

Key Features

  • Multi-Template Management: Handles an array of FlowTemplate objects, creating a dedicated FlowEngine for each.
  • Aggregated Operations: Methods like getItemsToActivate and getItemsToReset can aggregate results from all templates or filter by a specific template GUID.
  • Template Selection: Pass an optional templateGuid to operations to focus on a single template, or omit it to process all templates.

Usage Example

import { FlowEngineWrapper } from 'cliq-flow';
import type { FlowTemplate } from 'cliq-flow/types';

const templates: FlowTemplate[] = [
  onboardingTemplate,
  approvalTemplate,
  // ... more templates
];

const wrapper = new FlowEngineWrapper<MyItem>(templates, customDetector);

// Get activatable items across all templates
const allActivatable = wrapper.getItemsToActivate(items);

// Get activatable items for a specific template
const specificActivatable = wrapper.getItemsToActivate(items, 'onboarding-template-guid');

// Get items to reset across all templates
const allResettable = wrapper.getItemsToReset(items);

// Get items to reset for a specific template
const specificResettable = wrapper.getItemsToReset(items, 'approval-template-guid');

This wrapper is particularly useful when your application needs to handle multiple workflow types simultaneously, providing a unified interface for activation and reset operations.

What Happens If Items Are Missing?

That is enough; the engine only reads slug and the detector result.

If a template node has no matching item, the engine fabricates a lightweight mock (id = -1) so the graph stays complete. Its status is inferred from a predecessor (if one exists) or defaults to pending. This lets downstream activation logic remain stable even with partial data loads.

Activation Logic Recap

An item is returned from getItemsToActivate when:

  1. Its current status is pending.
  2. It has no predecessors (start node) OR its predecessors satisfy the join condition.

Reset Logic Recap

getItemsToReset performs a breadth‑first traversal starting from restartNodeId and collects downstream nodes that are presently closed. Those items should be transitioned back to an earlier state by your application when a restart is initiated.

Reset Eligibility

canResetFlow(taskSlug) returns { canReset: boolean; resetLabel?: string } and is true only when the task slug maps to a node that is a successor (not the restart node itself) of restartNodeId in the template graph.

Minimal Integration Steps

  1. Model your template graph (FlowTemplate).
  2. Decide on / implement status detection.
  3. Instantiate FlowEngine with template (+ optional detector).
  4. Feed runtime items to itemsToFlowInstance / getItemsToActivate / getItemsToReset as needed.
  5. Use returned statuses to drive UI / orchestration.

Error Handling & Edge Cases

  • Null / undefined item passed to detector -> treated as pending.
  • Detector exceptions inside auto‑mapping predicates are swallowed and the next predicate is tried.
  • Unknown item shape without custom detector -> always pending until you supply logic.
  • Cycles in the template are not explicitly guarded; provide an acyclic graph for predictable activation.

Extending Auto Detection

Add a new predicate + detector pair to FlowItemStatusDetectorMappings (PR welcome) so future consumers get zero‑config support for your item type.

FlowItemStatusDetectorMappings.push({
  predicate: (item) => 'startedAt' in item && 'finishedAt' in item,
  detector: (item) => item.finishedAt ? 'closed' : item.startedAt ? 'open' : 'pending'
});

TL;DR

FlowEngine = (Template Graph + Items + Status Detection) -> Activation + Reset Insights. The engine is item‑agnostic; just give it a slug and a way to detect status.


Need more examples? Check the test fixtures under src/__tests__.

Change Log

See Changelog for more information.

Author

Brandon Thompson

License

Licensed under the MIT License - see the LICENSE file for details.

Development

Install dependencies and build:

 yarn install
 yarn build

Testing

This package now uses Vitest for unit tests.

Common commands:

 yarn test          # run test suite once
 yarn test:watch    # watch mode
 yarn test:cov      # with coverage (./coverage)

Test files live under src/__tests__ and use the *.spec.ts suffix.