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

masterlinq-cli

v0.2.0

Published

Masterlinq Studio CLI — develop storefront themes and import, edit, preview, schedule, and publish pages.

Readme

Masterlinq CLI (mlq)

Edit your Masterlinq Studio theme in VS Code (or any editor) and keep it in sync with Studio. The server keeps doing the compiling and previewing; mlq moves files and shows you the result.

npm install -g masterlinq-cli

mlq login                      # approve this computer in the retailer portal
mlq theme pull --theme holiday # download a draft theme into ./holiday (+ AGENTS.md for coding agents)
cd holiday && code .           # edit theme/ in VS Code
mlq theme dev                  # push on save + live draft preview in your browser
mlq theme doctor               # static preflight: fonts, bindings, orphans, links, media, alt text
mlq theme publish              # set the draft live when it's ready
mlq page import https://old.example/pages/about # content + media → page draft

Requirements: Node 22+, an Owner or Admin login on a Masterlinq storefront account.

What a theme is

A Studio theme is the theme/ folder: browser-side TypeScript/TSX components, CSS, and theme/settings/*.json content documents. mlq theme pull also brings down the read-only platform contract (core/), the build scripts, and the root config so the folder typechecks and IntelliSense resolves @core/* imports — but only theme/ ever goes back up.

Read theme/agent/BUILD_CONTRACT.md in a pulled theme for the authoring rules (browser-only code, the page registry in theme/index.ts, settings documents, imports must stay inside the workspace). mlq also writes an AGENTS.md (and a CLAUDE.md that imports it) into the project root so Claude Code, Codex, and Cursor start with those rules, the design-system contract, and the commands below. It is regenerated on every pull; a hand-written one is left alone.

Commands

| Command | What it does | | --- | --- | | mlq login | Opens the portal so you can approve this computer. Use --token <mlqt_…> on a machine without a browser (create the token under Storefront → Themes → Develop locally). | | mlq logout / mlq whoami | Forget or show the saved credentials. | | mlq theme list | Themes on the account, with which one is live. | | mlq theme create <name> [--from <id>] [--starter <id>] [--brief <file>] [--dir <path>] [--pull] [--install] | Create a draft (empty template, a copy of another theme, or a starter). With --brief, pull it, apply the design brief to the brand profile, and push that as the first change set. | | mlq theme rename <name> | Rename the current project's theme (or --theme <id>). | | mlq theme pull [--theme <id>] [--dir <path>] [--install] | First run: download the theme into a folder. Inside a project: bring theme/ up to date with Studio. --full re-downloads the whole archive (refreshes core/), --force overwrites local edits, --install runs npm install afterwards. | | mlq theme status | Local changes and anything that changed in Studio since your last sync. | | mlq theme push [-m <message>] [--dry-run] [--force] [--no-doctor] | Push local changes as ONE change set (one History entry in Studio, one typecheck). Runs the doctor first (advisory) and prints the preview link after. Refuses to overwrite files Studio changed since your last sync unless --force. | | mlq theme dev [--port <n>] [--no-open] | Watch theme/, push every save, wait for the draft bundle to rebuild, and swap the theme in place in a local preview page. Studio shows "CLI connected" while it runs. | | mlq theme open [--print] [--share [--days <n>]] | Open the draft preview (or the live storefront). --print only prints the link; --share mints a customer preview link that needs no Studio login. | | mlq theme publish [--yes] | Set the draft live. The server typechecks and builds first; a broken draft never goes live. | | mlq theme doctor [--json] [--static] [--rules] | The static preflight (below), plus the full toolchain build when node_modules is present. Exit 1 on errors. | | mlq theme check | The server's complete build, locally, in the same order: import containment → TypeScript → Studio bindings → theme quality → bundle build → design-system scan (monitor). Needs npm install. | | mlq theme clean [--dry-run] [--yes] | Remove settings files and components nothing renders (what doctor reports as orphans). Asks first. | | mlq theme history [-n <count>] [--json] | Change sets on the theme — CLI pushes, Studio saves, agent runs, reverts — newest first. | | mlq theme rollback <change set> [--yes] | Restore every file of a change set to how it was before it, as a NEW change set (so a rollback can be rolled back), then pull the restored files down. | | mlq theme inspect <url> [--out <file>] | Scan a public website into a design brief: header/nav/section/footer structure, fonts, asset counts (server-side browser captures) plus the site's most-used colours. | | mlq theme brief init / mlq theme brief apply <file> | Write an empty brief to fill in; apply a brief's colours, fonts, shape, identity, and voice to theme/settings/ds/brand/* and register the fonts in theme/index.ts. | | mlq theme add <kind> <Name> [--force] | Scaffold a section: header, mega-menu, product-rail, editorial-grid, mobile-menu. Settings document + component, bound for visual editing, built from the design-system primitives. | | mlq media import <url…> [--name <name>] [--alt <text>] [--json] | Copy public image/video URLs into the account's media library and print their CDN URLs — the URL a theme settings file should reference. | | mlq media upload <file…> [--name <name>] [--alt <text>] [--json] | Upload local image/video files (JPG, PNG, GIF, WebP, SVG, MP4, WebM, MOV) into the media library and print their CDN URLs. | | mlq media import --manifest <file> [--dry-run] | Bulk import/upload from a manifest with names, folders, alt text, attribution, and the settings pointer each asset belongs in (below). | | mlq page list [--json] | List account-owned Studio pages and their draft, active, and scheduled state. | | mlq page pull <slug\|pageId> [--out <file>] [--force] | Download editable content to pages/<slug>.json in the project root, outside the theme bundle. Refuses to overwrite a file that differs unless forced. | | mlq page create <title> [--slug <slug>] [--template <key>] | Create an empty Mongo-backed page draft and local JSON file. | | mlq page push <file> [--force] | Push page content into its draft. Stale revisions are refused unless deliberately forced. | | mlq page import <url> [--slug <slug>] [--template <key>] [--no-media] [--dry-run] | Rebuild an old-site page as a Studio page draft: Shopify sections map to Studio sections (slideshow → hero banner, image-with-text, video; the rest as rich text), header/footer chrome is dropped, images are copied into the media library, SEO comes along, and every same-site link is rewritten to the new storefront's paths. Product links keep their handle and resolve by search until pointed at the exact product; the summary lists them. --dry-run prints the sections and links without creating anything. | | mlq page delete <slug\|pageId> [--yes] | Permanently delete a page, draft and active snapshot alike (asks first). | | mlq page open <slug> [--theme <id>] | Open the page through a signed Studio preview, including unpublished content. | | mlq page publish <slug> / unpublish <slug> | Promote the current draft snapshot or remove the page from public traffic. | | mlq page schedule <slug> --at <ISO date> / cancel-schedule <slug> | Schedule or cancel publication through Studio's shared publisher. | | mlq nav list [--json] | Navigation menus with handle, title, and item count. | | mlq nav pull [--out <file>] [--force] | Download every menu to navigation.json in the project root (never under theme/). Refuses to overwrite a file that differs unless forced. | | mlq nav push <file> [--menu <handle>] [--force] | Replace the navigation (or one menu with --menu) after printing what changes. Validated locally first; stale revisions are refused. Live immediately. | | mlq nav delete-menu <handle> [--yes] | Delete a menu (main and footer are protected; asks first). | | mlq nav open [<handle>] [--theme <id>] [--print] | Open the storefront preview to see the menus rendered by the theme. | | mlq group list [--json] | Product groups as a tree: name, slug, source, product count, and the menus that link to each. | | mlq group pull <slug\|id> [--out <file>] [--force] | Download a group to groups/<slug>.json in the project root. | | mlq group create <name> [--slug] [--parent <slug\|id>] [--type <t>…] [--brand <b>…] [--match any\|all] [--sku <sku>…] [--display product-list\|landing-page] [--menu <handle>] [--include-children] [--allow-unknown] | Create an automated (types/brands) or manual (SKUs) group, optionally nested and linked from a menu; --include-children makes it a parent container that also pulls in every child group's products. Writes the local file. | | mlq group push <file> [--force] | Merge a local group file into Studio. Stale revisions are refused unless deliberately forced. | | mlq group delete <slug\|id> [--yes] | Permanently delete a group; child groups move to the top level and its menu links are removed (asks first). | | mlq catalog query --collection <slug> --brand <name> --type <name> [-n <limit>] | The product query a section should use, in every form: the useProducts object, the /api/data/products URL, and the v1 path. mlq catalog facets lists the names it accepts. | | mlq catalog facets [--types] [--brands] [--collections] [--search <text>] [--json] | The catalog's product types, brands, and collection slugs — the exact names --type / --brand / --collection must use. Selectors restrict the sections; --search is a case-insensitive substring filter. |

Exit codes: 0 ok · 1 error (or doctor errors) · 2 push refused because of conflicts · 3 push landed but TypeScript failed.

Doctor

mlq theme doctor runs on the files alone — no toolchain, no browser, under a second — so push runs it every time (advisory; --no-doctor skips it). Errors:

  • fonts/unregistered — a family set in ds/brand/typography.json is not in the theme's fonts list (nothing loads it; the browser silently falls back).
  • fonts/css-import — Google Fonts @imported in CSS or <link>ed in TSX instead of registered.
  • bindings/missing — a schema-backed settings file with no studioSection(...) binding (invisible to visual editing; the server's gate fails the push).
  • pages/meta-missing — a customPages slug without a pages-meta.json entry (404s on the server).
  • media/other-account — an image URL from another account's media library.
  • images/alt — an <Image>/<img> without alt.
  • settings/invalid-json.

Warnings: settings/orphan and components/orphan (nothing reachable from theme/index.ts uses it; theme clean removes them), pages/meta-stale, links/unregistered-page (/pages/<slug> that is not a theme custom page — fine if the portal serves one), links/placeholder (href="#"), media/external (media hosted outside the account's library).

mlq theme doctor --rules lists them; --json is the machine form. Rendered checks (mobile overflow, image cropping) need a browser and are not part of doctor.

Media

A theme must never point at another site's images. Bring assets into the account's media library first and reference the CDN URL the command prints:

mlq media import "https://old-site.example/hero.jpg" --name hero-1 --alt "Rider on the trail"
mlq media upload ./logos/*.png --json

Both accept several sources at once and exit non-zero if any of them failed. Images are capped at 10 MB, videos at 50 MB.

For a whole site's worth, write a manifest and let the CLI wire the results straight into settings:

[
  { "file": "./hero-1.jpg", "name": "hero-1", "folder": "home", "alt": "Rider on the trail",
    "attribution": "Photo: J. Doe",
    "settings": "theme/settings/home-hero.json", "pointer": "/data/slides/0/image" },
  { "url": "https://old-site.example/logo.png", "name": "logo-dark",
    "settings": "theme/settings/ds/brand/identity.json", "pointer": "/data/logo" }
]
mlq media import --manifest media.json --dry-run   # show the plan
mlq media import --manifest media.json             # upload/import, then set each pointer to the CDN URL

folder becomes a hyphenated prefix on the name (home-hero-1; the library keeps only letters, digits, and hyphens). attribution is reported, and written into settings when the entry also has an attributionPointer. Settings files are only rewritten when every asset they need succeeded.

Account-owned pages

Page content and page design have separate lifecycles. pages/*.json contains portable, Mongo-backed content: title, slug, SEO, template key, sections, and revision metadata. theme/pages/ContentPage.tsx and the rest of theme/ decide how those records look. Changing themes changes presentation without discarding the retailer's pages.

mlq page import https://old-shop.example/pages/custom-builds
# edit pages/custom-builds.json
mlq page push pages/custom-builds.json
mlq page open custom-builds
mlq page schedule custom-builds --at 2026-10-01T09:00:00-07:00

Page files default to pages/<slug>.json under the mlq project root (the folder holding .mlq/, or the current folder outside a project), wherever the command runs from. They are never written under theme/ — even with --out — because theme push and theme dev would upload them as theme code.

Imports remove the source site's navigation, footer, scripts, forms, event handlers, and styling. Editorial headings, paragraphs, lists, links, and images remain. Images are copied to the account media library unless --no-media is passed. Imports always create drafts; publishing is a separate explicit command. Before any media is copied the importer checks that the slug is free and that the content fits one section (262,144 characters of section settings — the server's limit; a 3 MB source page can exceed it). If the content still fails to land after the draft is created, the file is written locally regardless: a content rejection (HTTP 400) keeps the empty draft so you can trim the file and page push it, any other failure deletes the empty draft, and the message says which.

Each pulled file carries draftRevision. If the page changes in Studio after a pull, page push exits with code 2 and asks you to pull again (by page id, since the slug may have changed). --force deliberately applies the local document over the latest draft. Neither action changes the active snapshot until page publish or the scheduled time.

Server-owned fields

The file is the full page record, so it also carries fields the server owns: draftRevision, publishedRevision, status, hasDraftChanges, createdAt, updatedAt, publishedAt, and scheduledPublishAt. Leave them alone — the server recomputes them on every push and ignores edits to them. draftRevision in particular is the conflict guard: it must stay exactly as pulled, or the push is refused. Edit title, slug, template, seoTitle, seoDescription, and sections; the lifecycle fields change through page publish, unpublish, and schedule.

Storefront navigation

Navigation is one live document per account — there is no draft, so a push is visible to shoppers at once. navigation.json holds every menu; items are flat, each with a parentId (or null) and an order:

mlq nav pull                                # → navigation.json in the project root
# edit navigation.json
mlq nav push navigation.json                # every menu; prints + added / ~ changed / - removed first
mlq nav push navigation.json --menu footer  # just one menu
mlq nav open                                # see it rendered by the live theme
{
  "format": "masterlinq-studio-navigation",
  "formatVersion": 1,
  "revision": "638600000000000000",
  "updatedAt": "2026-09-14T22:00:00Z",
  "menus": [
    { "id": "main", "handle": "main", "title": "Main menu", "items": [
      { "id": "bikes", "label": "Bikes", "type": "category", "categoryId": "…", "includeChildren": true, "parentId": null, "order": 0 },
      { "id": "road", "label": "Road", "type": "category", "categoryId": "…", "parentId": "bikes", "order": 0 },
      { "id": "about", "label": "About", "type": "page", "pageId": "…", "parentId": null, "order": 1 },
      { "id": "blog", "label": "Journal", "type": "custom", "url": "/blog", "openInNewTab": false, "parentId": null, "order": 2 }
    ] },
    { "id": "footer", "handle": "footer", "title": "Footer", "items": [] }
  ]
}

Item types and the field each needs: custom → url (a /path or http(s)://…), page → pageId, category → categoryId (a group id from mlq group list --json), product → productId, brand → brandName, policy → policyType, location → locationId, blog → blogArticleId or blogSlug, helpCenter → any of helpArticleId / helpArticleSlug / helpCategoryId / helpCategorySlug. Handles are a-z, 0-9, and hyphens (up to 60), unique per account; main and footer can be emptied but never removed or renamed; a menu holds up to 500 items with unique ids, parentId pointing at an item of the same menu, and no cycles. push checks all of this before sending and names the menu and item at fault; the server applies the same rules. url on category items, displayType, imageUrl, and productFilter are recomputed by the server on save — leave them alone.

revision is the conflict guard: if the navigation changed in the portal after the pull, push exits with code 2 and asks you to pull again; --force deliberately applies the local document. --menu <handle> sends only that menu (it is created if it does not exist yet) and leaves the rest of the file for a later push; the command warns when other menus in the file still differ from Studio.

Product groups

A product group (a collection at /collections/<slug>) is the category record — name, slug, parent, display type — plus the rule that selects its products. The CLI presents both as one file, groups/<slug>.json:

mlq group list                                              # the tree, with product counts and menus
mlq catalog facets --types --search bike                    # the exact type names the catalog uses
mlq group create "Road Bikes" --type "Road Bikes" --brand Trek --brand Specialized --menu main
mlq group create "Staff Picks" --sku SKU-1 --sku SKU-2 --parent road-bikes --display landing-page
mlq group pull road-bikes
# edit groups/road-bikes.json
mlq group push groups/road-bikes.json
mlq catalog query --collection road-bikes --limit 8         # the query a section should use

--type / --brand make an automated group: they become matchRules (--match all requires every one; the default any is enough for one), and the server writes the matching rule. --sku makes a manual group: an "any of these SKUs" rule, exactly as the portal's manual mode stores it. A group is one or the other. --menu <handle> also appends a category link for the group to that navigation menu.

Types and brands have to equal the catalog's own names, or the rule matches nothing — so create checks every --type and --brand against mlq catalog facets first (case-insensitively, ignoring surrounding whitespace) and sends the catalog's spelling (--brand specialized becomes Specialized). An unknown value is refused with up to three close matches; mlq catalog facets --types --search <text> browses the list. The facet list is cached for a few minutes on the server, so a brand or type added moments ago can be missing: --allow-unknown sends the value as given. On an older server without the facets endpoint the check is skipped with a warning. push runs the same check on matchRules but only warns, since a pulled file may carry names the catalog has since renamed.

In the file, edit name, slug, description, parentId (a group id, or null for the top level), order, displayType (product-list or landing-page), includeChildProducts, matchRules, and rule. rule is what selects products: to change an automated group's filters, edit matchRules.productTypes / matchRules.brands and set rule to null so the server regenerates it — push refuses a file whose simple rule and matchRules disagree, because one of them would silently win. A hand-written rule (nested groups, other fields such as variant.Title or variant.MSRP) is sent as written. matchRules.collections always follows the slug. id, level, sourceType, menus, stats, createdAt, updatedAt, and revision are server-owned; menus lists the handles whose menu links point at the group (edit those links with mlq nav).

Pushes are merges keyed by the group's id (so renaming the slug in the file renames the collection). A stale revision exits with code 2 and asks you to pull again by id; --force re-reads the revision and applies the local file. Slugs are a-z, 0-9, and hyphens (up to 120) and unique per account; a group cannot be moved under itself or one of its descendants.

Design briefs

mlq theme inspect https://www.example.com/         # → example.com-brief.json
# fill in colors (hex), confirm fonts, pick layout options
mlq theme create "Example" --brief example.com-brief.json --dir ./example

inspect asks the server to capture the site at desktop and mobile widths and extract what it is made of — header rows, nav labels, section kinds and headings, rails, footer columns, socials, fonts, asset counts — and adds the most frequent colours in the site's CSS as paletteCandidates. Nothing is assigned automatically (the most common colour is usually white); you fill colors, and brief apply writes them into the brand profile, validates layout options against the theme's own schema, and registers the Google fonts. structure is documentation for whoever builds the sections; the storefront's sections are built from it, not copied.

How sync works

  • .mlq/state.json (git-ignored) remembers the content hash of every theme file as of the last sync. push sends each change with that hash as its base version; the server rejects the write if the file moved on in Studio (someone edited it in the editor, or the agent did). pull does the same in reverse and leaves files that changed on both sides alone.
  • Every push carries a client-generated operation id; the server echoes it, logs it, and records it on the theme. If the CLI dies after the server committed (laptop sleeps, connection drops), the next status, push, or pull reconciles: versions are content hashes, so a file whose local and server versions already match is adopted into the baseline instead of being reported as changed on both sides or refused as a conflict.
  • Every push lands atomically and shows up in Studio's History panel as a revertable change set. Studio polls the theme's activity and refreshes its preview when a CLI push lands (unless someone has unsaved work there, in which case it offers the reload instead).
  • The live theme is read-only on the server. Pull a draft (or mlq theme create --from <live-id>) to edit; publish swaps it in.
  • Some files are generated by the server and are pull-only: theme/styles/brand-tokens.css (from the brand profile), the Features installer ledgers, and the compliance cache. mlq never pushes them; edit their source in Studio instead.
  • Only .tsx .ts .css .md .json under theme/, at most 400 KB each. Anything else is skipped with a note. Add a .mlqignore (one glob per line, relative to the project root) for scratch files.

mlq theme dev

The dev page at http://127.0.0.1:<port>/ embeds the same draft preview Studio uses. After each push the CLI waits for the server to rebuild the draft bundle, then tells the page to swap the theme in place — no reload, scroll and cart survive. TypeScript errors are printed in the terminal (the push still lands, so keep going and fix them before you publish). Draft preview links expire after a few hours; the CLI refreshes them for you. While it runs, the CLI heartbeats every 30s so Studio's footer shows CLI · your-machine · watching now and warns anyone editing there that edits to the same files will conflict.

Local typecheck and IntelliSense (optional)

Run npm install inside a pulled theme (or pull with --install) to get editor IntelliSense for @core/* and to run mlq theme check / the doctor's toolchain gates locally. The platform's core/ layer depends on @masterlinq/cart-checkout, which is published to GitHub Packages, so this needs a read:packages token in your ~/.npmrc until the package is public. Pushing, previewing, and publishing never need it.

Credentials and CI

Credentials live in ~/.config/masterlinq/credentials.json (mode 600). For CI or scripts set MLQ_TOKEN (and MLQ_API_URL for a non-production API); it overrides the file. Tokens are revocable from the portal at any time.

# GitHub Actions: push the theme branch to a Studio draft on every commit
- run: npx masterlinq-cli theme push -m "CI ${{ github.sha }}"
  env:
    MLQ_TOKEN: ${{ secrets.MLQ_TOKEN }}

Development

npm install
npm run typecheck
npm test          # builds, then runs the node:test suite against an in-process mock API
node dist/bin.js --help