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

@brftech/filex-core

v0.51.0

Published

filex core - Vue 3 source of truth for the filex file manager (FileExplorer + ConnectionsPanel SFCs, composables, types)

Readme

@brftech/filex-core

Vue 3 source of truth for the filex file manager. Ships the <FileExplorer> SFC, the <ConnectionsPanel> surface, the composables that drive them, and the type definitions consumers (Vue apps, the @brftech/filex Web Component wrapper, the @brftech/filex-react adapter) build against.

Looking for a drop-in <filex-explorer> HTML tag? Use @brftech/filex. React idiom? Use @brftech/filex-react.

Install

npm i @brftech/filex-core vue

vue ^3.4 is a peer dependency. The following are optional peers - features degrade gracefully if missing:

| Peer | Used for | |---|---| | monaco-editor | Code edit/view (top-tier IDE-grade) | | highlight.js | Read-only code colour fallback while Monaco loads, or as the permanent renderer when Monaco isn't installed | | markdown-it | Markdown preview | | codemirror + @codemirror/lang-* | Lighter-weight editor alternative |

Every preview that draws a file as HTML (Markdown, notebooks, highlighted code, Mermaid, KaTeX) sanitizes it with DOMPurify, a regular dependency of this package (bundled unmodified; licensed Apache-2.0 OR MPL-2.0 at your choice).

Use

<script setup lang="ts">
import { FileExplorer } from '@brftech/filex-core';
import '@brftech/filex-core/style.css';

const config = {
  // Modern API (RESTful):
  apiBase: 'https://files.example.com',
  auth: { kind: 'bearer', token: '<jwt>' },

  // Or legacy Vuefinder-compat:
  // endpoint: '/api/files/manager',
  // uploadInit: '/api/files/upload/init',
  // …

  locale: 'tr',
  theme: 'auto',
  trashVisible: true,
  sideNav: true,          // the navigation panel (default on)
  connections: true,      // its "How to connect" + "API keys" entries
  uiProfile: 'standard',  // 'simple' - one pane, list/grid, no tabs
};
</script>

<template>
  <FileExplorer
    :config="config"
    @error="(e) => console.error(e)"
    @file-opened="(f) => console.log('opened', f)"
    @share-created="(s) => navigator.clipboard.writeText(s.url)"
  />
</template>

Auth

type AuthConfig =
  | { kind: 'bearer'; token: string | (() => string | Promise<string>) }
  | { kind: 'csrf'; csrf: string }
  | { kind: 'basic'; user: string; pass: string }
  | { kind: 'none' };

Function-token bearers are awaited on every request so silent JWT refresh just works.

⚠ A cookie session from another origin (csrf or none, riding on filex's own session cookie): since filex 0.50 a change the browser sends that way is refused (403 cross_origin_refused) unless the host page's origin is in the server's FILEX_CORS_ALLOWED_ORIGINS by name - * does not grant it, and a sibling subdomain is another origin. A bearer token, or a host that proxies with a key, needs nothing (CONFIGURATION.md → Requests from other origins).

What the account may do. Against a filex server with roles and per-user permissions (0.49+), the explorer reads the signed-in account's permissions from GET /api/auth/me itself and hides what its role refuses - Delete, Rename, Share and the rest - so an embed hides exactly what the filex web app hides, with nothing to wire. Pass permissions (and permissionsByFolder) in the config only if your host already holds the answer, or me to point the request elsewhere. An API token behind the embed is held to its own verbs as well: read alone is a read-only explorer (PERMISSIONS.md, RBAC.md → API tokens).

API surface

import {
  FileExplorer,
  useFileApi, useUploadChunked, useSelection, useKeyboardShortcuts,
  useLocale, usePendingOps, useMonacoLoader,
  preloadEditor, ensureMonaco,
  // naming a key on screen - read the binding, never type it out:
  // shortcuts are remappable, so a hardcoded "Ctrl+K" stops being true
  shortcutHint, eventMatchesShortcut,
  // how far a queued copy/move/delete has got - bytes when a transfer between
  // two storages reports them, `null` when there is no honest percentage
  opPercent,
  // types
  type ExplorerConfig, type AuthConfig, type FileNode, type ShareInfo,
  type Capabilities,
} from '@brftech/filex-core';

The composables are stable - feel free to compose your own UI without touching the SFC. The end-to-end encryption building blocks the explorer uses (encrypted names, the STREAM format for large files, converting a folder, a .fxe's new password) are exported too (E2E-ENCRYPTION.md → Using the building blocks), as are the draft helpers (API.md → Composables) and markdownToSafeHtml, the sanitised Markdown the explorer's preview draws (nothing in it runs). Since 0.51 the admin panel's mega menu is exported as MegaMenu with its helpers (pruneMegaMenu, entryIsCurrent, the MegaMenuEntry types): the host passes the entries it lets the reader open and does the navigating itself (ADMIN-PANEL.md).

If your own UI names a keyboard shortcut, render shortcutHint('<action>') rather than the key itself: the user may remap any action from the shortcut settings, and '' comes back for one they unbound so you can drop the hint instead of drawing an empty key cap. See docs/API.md.

If you draw your own progress for queued operations (usePendingOps), take the percentage from opPercent(op) rather than done / total: those count the selected items, so moving one large file reads 0 / 1 until it ends. A null means draw a moving indicator, not 0%.

Navigation panel

The explorer ships a left navigation panel - the primary + New menu (upload files · new folder · new document · request files), the destinations Home · Shared with me · My shares · Recent · Starred · Trash (plus My files when the caller reaches at most one storage), your tags in two groups (Personal and Team), an Apps section with one row per installed app's own page, and the storages the caller can see (a storage reached through a grant is marked Shared). It is on by default on every surface; the viewer collapses it to an icon rail from the control at the far left of the top bar - above the panel rather than inside it, so it is still reachable once the panel is a rail - and that choice is remembered per browser. Under 560px it becomes a drawer over the listing instead of a column.

const config = {
  apiBase: 'https://files.example.com',
  auth: { kind: 'bearer', token },
  sideNav: true,          // default; `rootPath` flips it off
  uiProfile: 'simple',    // 'standard' (default) | 'simple'
  mySharesVisible: true,  // draw the "My shares" row - only if you handle @open-my-shares
  appHomePage: true,      // an app's home view opens as YOUR page - handle @open-app-home
};

⚠ mySharesVisible and appHomePage are off by default, and not because the surfaces are optional: each needs the host to take an event and open a page of its own - @open-my-shares for the links this person made, and @open-app-home for an app's home view ({base}app/{plugin}/{view}, with the open section in ?section=). Only this Vue component emits them; the web component and the React adapter do not forward them yet, so switching the keys on there would draw a row that goes nowhere.

The panel's last section is how How to connect (the per-protocol guides, built from your deployment) and API keys (mint and revoke the tokens WebDAV, FTPS and filex mount sign in with) become reachable from inside the explorer at all - ConnectionsPanel and TokensPanel were exported from this package long before anything opened them, so an embedded explorer's users had to be told to ask an administrator. Set connections: false to leave them out. ⚠ Never gated on role in the UI: /api/tokens caps every scope against the caller's own account, and the panel renders what the API returns.

⚠ API keys is dropped for an app token - along with Recent, Starred and Shared with me - because those surfaces belong to one person and an app token belongs to none. ConnectionsPanel degrades with it: the guides stay, and S3KeysPanel / SSHKeysPanel / NFSExportsPanel / TokensPanel show their existing "cannot mint" note instead of a form, driven by the server's 403 through the canMint / canAdd flag each composable already reports. That is callerKind, read from GET /api/files/capabilities (caller_kind) and overridable per embed; it is a credential-kind check, not the role check the paragraph above forbids. "How to connect", Upload, the storages and Trash stay. See docs/MCP.md → Token kinds.

uiProfile: 'simple' is a preset, not a feature switch - nothing is removed from the build. It turns off the tab strip and the split pane, reduces the view switcher to list + grid, and defaults the panel's "How to connect" / "API keys" entries off, for the people who want a file drive rather than a file manager. It does not gate the navigation panel: that ships in every profile, and only the viewer's own collapse choice moves it.

There are two profiles and no third. ⚠ Anything else that reaches uiProfile - a typo, or the 'drive' profile that was removed after v0.40.0 - resolves to 'standard' and logs one console line naming it. If you were passing 'drive', pass 'simple'. It was only ever simple plus a look, and the look below is now what every embed draws, with no string passed:

  • one primary + New menu in the panel (upload files · new folder · new document · request files) instead of the Upload / New folder pair,
  • one search field across the header with a ⌘K / Ctrl+K chip that hands the query to the command palette - the field searches the folder you are in, the palette is where "everywhere", saved searches and commands live,
  • a filter row under the breadcrumb: Type · People · Modified · Size,
  • Folders and Files as labelled sections in grid view - replaced by date headings (Today · Yesterday · This Week · This Month · September 2026) in all three views while the listing is sorted by Modified,
  • the details panel split into Details and Activity, with "People with access" and a share-link row,
  • a storage line under the navigation (GET /api/files/quota/me).

The People chip and the Owner column are real now: migration 00038 put the owner on the node itself, so a listing row carries one, the chip offers only the people who actually own something in the rows on screen, and quota is counted against the owner rather than whoever last touched the file.

Connections

A second surface, for reaching the same server without a browser. filex can be spoken to as S3, SFTP, FTPS, NFSv3 and WebDAV, and mounted with filex mount; <ConnectionsPanel> is where a user manages storages, mints the credential each protocol takes, and reads instructions built from this deployment - its host, its port, their login - rather than a template with angle brackets in it.

import {
  ConnectionsPanel,          // the whole surface: storages + guides
  S3KeysPanel,               // or mount the pieces yourself
  SSHKeysPanel,
  NFSExportsPanel,
  TokensPanel,               // FTPS / WebDAV / filex mount sign in with a token
  ConnectionGuideView,
  buildGuide, guideProtocols,
  useS3Keys, useSSHKeys, useNFSExports, useTokens,
  type ProtocolGuide, type ApiToken,
} from '@brftech/filex-core';

⚠ Mount the panel, not a copy of it. The admin panel, the web explorer and the filex desktop app all render this component - a surface that mints credentials one of them cannot see or revoke is the failure mode the shared package exists to prevent.

See docs/PROTOCOLS.md.

Live updates

An embedded explorer keeps itself current over a WebSocket: it mints a short-lived ticket, opens the socket and re-lists a folder when something in it changes. Two things are worth knowing before you host it.

Proxy /api/ws. If your page reaches filex through your own backend, the ticket route is under /api/files/ and the socket is not - a proxy rule that only forwards /api/files/* leaves the explorer with no socket, and it falls back to re-listing every 12 s. It keeps working, quietly, which is why this is easy to ship without noticing.

A burst is one frame per window, not one frame per file. The server sends the first change in a quiet folder immediately and merges everything after it into one frame per window (200 ms, stretching to 1.5 s while the burst continues); a merged frame carries count. Nothing is dropped - the last frame of a burst always reflects the final state. If you debounce on your side as well, give your debounce a ceiling: a plain trailing debounce starves under a sustained stream, because every arriving frame cancels the pending reload. The package's own burstDebounce does exactly that.

Full contract: docs/REALTIME.md.

Build

pnpm build       # vue-tsc + vite lib build → dist/
pnpm typecheck

Output:

  • dist/filex-core.js (ESM)
  • dist/filex-core.umd.cjs (UMD)
  • dist/style.css
  • dist/index.d.ts (rolled-up declarations)

License

MIT