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

@doubling/compound-sync

v1.16.6

Published

Bidirectional sync between Compound and local markdown files

Downloads

1,109

Readme

Compound Sync

Bidirectional sync between Compound and local markdown files. Edit files locally in your favorite editor and they sync to Compound in real time.

Quick Start

npx @doubling/compound-sync

That's it. A browser window will open for sign-in (Google or email/password), then the setup wizard will walk you through selecting your organization and sync folder.

Prerequisites

TLS certificates

On Node 22+ the daemon auto-detects the OS system certificate store (/etc/ssl/cert.pem on macOS, /etc/ssl/certs/ca-certificates.crt or /etc/pki/tls/certs/ca-bundle.crt on Linux) and re-execs itself with NODE_EXTRA_CA_CERTS set; otherwise fetch() to Google Identity / Firebase Auth endpoints fails with auth/network-request-failed because Node's bundled CA store doesn't match what those services use.

If you're behind a corporate proxy with custom roots, set NODE_EXTRA_CA_CERTS explicitly to your bundle and the daemon will use that instead:

NODE_EXTRA_CA_CERTS=/path/to/corporate-bundle.pem npx @doubling/compound-sync

What it does

Compound Sync watches a local folder and your Compound workspace simultaneously. Changes in either direction are synced automatically:

  • Edit a file locally → it updates in Compound
  • Edit a file in Compound → it updates locally
  • Create or delete files in either place → synced

Local folder structure

{Sync Folder}/
  {TeamName} Teamspace/   -- team files (bidirectional)
  Private/                 -- your private files (bidirectional)
  Shared by Me/            -- symlinks to files you've shared
  Shared with Me/          -- files shared with you (read-only)

Hidden sync metadata

The daemon maintains per-machine state under your sync folder (dotfiles and directories, not uploaded to Compound):

| Path | Purpose | | ---- | ------- | | .compound-sync/manifest.json | Maps synced paths to Firestore file IDs (startup reconcile) | | .compound-sync-state.json | Content-hash baseline for push/pull decisions | | .compound-yjs-binding/ | Offline Yjs merge state per file |

Do not edit these by hand. See docs/features/sync/local-metadata.md.

Running

After setup, start syncing with:

npx @doubling/compound-sync

The sync daemon runs until you press Ctrl+C.

Multiple workspaces

If you're a member of more than one workspace, you can sync them all from a single daemon. During --setup you'll be asked which workspaces to sync (comma-separated or all) and given a per-workspace local folder prompt.

You can also keep multiple separate configs (one per workspace, or one for "all my workspaces") with --config pointing at an absolute path:

# Setup
npx @doubling/compound-sync --config ~/.config/compound-sync/work.json --setup

# Run
npx @doubling/compound-sync --config ~/.config/compound-sync/work.json

--config accepts absolute paths (with ~ expanded), so you can keep configs outside the package install directory. Bare filenames still resolve relative to the package install for backward compatibility.

Security

  • First sign-in is interactive (browser popup, Google or email/password). After that the daemon runs silently: it persists the Firebase refresh token locally so relaunches don't re-prompt.
  • Persisted credentials live at ~/.config/compound-sync/{project}__{account}.json, written mode 0600 (owner read/write only) via an atomic tmp+rename. The desktop app overrides the path per-account with COMPOUND_AUTH_FILE. See auth-persistence.ts.
  • The config file (config.json, config-*.json) holds no credentials — only projectId and org→localPath mappings.
  • All data access uses the Firebase client SDK with App Check and the same Firestore/Storage rules as the web app — the daemon has no Admin-SDK elevation and cannot see beyond the signed-in user's org memberships. See ../docs/security/09-sync-daemon.md.

Internal Development

For Doubling team members testing against sandbox or dev environments:

# Setup
npx @doubling/compound-sync --env sandbox --config config-sandbox.json --setup
npx @doubling/compound-sync --env dev --config config-dev.json --setup

# Run
npx @doubling/compound-sync --env sandbox --config config-sandbox.json
npx @doubling/compound-sync --env dev --config config-dev.json

Config files (config-*.json) are gitignored and stored locally.

Tests

# Pure unit tests (no emulator required)
npm run test:unit

# Integration tests (boots firestore + storage emulators)
npm run test:integration

# Both
npm test

TypeScript conventions (DOU-181)

sync/ is being migrated to TypeScript file-by-file. The conventions:

  • Source layout: in-place. Each .ts file emits .js, .d.ts, and source maps as siblings via tsc with outDir: ".". The emitted .js is a gitignored build artifact, never edited by hand; the .ts is the source.
  • Imports always use the .js extension (NodeNext convention). At runtime: in dev/test the tsx loader maps ./paths.js to ./paths.ts so source runs without a build step; in production the compiled paths.js exists alongside and resolves directly.
  • Dev (no build needed): npm run sync runs through node --import tsx, so .ts files load on demand.
  • CI / publish: npm run build compiles every .ts source to its .js sibling. prepack runs build automatically before npm publish so the npm tarball ships compiled JS. The desktop staging script also runs npm run -w sync build before copying.
  • Typecheck: npm run typecheck (alias for tsc --noEmit) at the workspace level, or at repo root npm run typecheck which runs web + sync together.
  • Strictness: strict: true, noUncheckedIndexedAccess: true, noImplicitOverride: true. No any. No // @ts-ignore. If a third-party module lacks types, declare its shape in a .d.ts or PR types upstream.

Logging convention

Org-scoped output: [${orgId}] [tag] message. Use console.warn for recoverable issues, console.error for failures. Tags like [local scan] and [manifest reconcile] identify subsystems.

Phase 1 migrated paths.js to paths.ts as proof of pattern. Phase 3 ([[DOU-183]]) migrates the remaining source files; everything in sync/ will be .ts by the end.

Building & running from source

sync.js (and the sibling *.js files listed in package.json's files) are build artifacts, gitignored, never edited by hand. Run from the .ts source with the tsx loader:

# Dev (no build; tsx maps ./foo.js → ./foo.ts on demand)
npm run sync -- --setup --env sandbox      # first-run wizard against sandbox
npm run sync -- --env sandbox              # start the daemon

# Compile every .ts to its .js/.d.ts sibling (tsc, outDir ".")
npm run build

# Typecheck only
npm run typecheck

--env takes a short name (sandbox | dev | prod); it is expanded to doubling-compound-<name> internally (sync.ts). Omitting --env defaults to prod.

Publishing to npm

@doubling/compound-sync is published automatically by .github/workflows/publish-sync.yml on every GitHub Release whose tag starts with @doubling/compound-sync@ (Changesets creates the release + version bump). Highlights:

  • Trusted Publishing (OIDC): no long-lived NPM_TOKEN — GitHub mints a short-lived OIDC token that the npm CLI (≥ 11.5.1) exchanges for a publish-scoped token.
  • prepack runs npm run build, so the tarball ships compiled JS.
  • Idempotent: the workflow checks npm view and skips cleanly if sync/package.json's version is already on npm.
  • Kept in lockstep with the desktop .dmg built from the same commit (DOU-159), so npx @doubling/compound-sync and the bundled daemon are identical.

Do not npm publish by hand; cut a Changesets release instead.

Design docs