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

@g9tm/syncdata

v1.5.12

Published

TypeScript twin of the G9SyncData .NET client for Lynx, Node and browsers: the sync engine, the G9RowBatch protocol, the SQLite client store (same schema as the .NET store) and the SignalR transport.

Readme

@g9tm/syncdata

The TypeScript twin of the G9SyncData .NET client: offline-first sync between a device's SQLite database and a PostgreSQL server, for Lynx apps (with @g9tm/syncdata-lynx), Node and browsers. Same protocol, same engine behaviour, and the same local database as the .NET client — a database the MAUI app wrote opens in the Lynx app and keeps syncing.

| Entry point | What | |---|---| | @g9tm/syncdata | the engine (G9SyncEngine), the client model, the protocol (DTOs, G9RowBatch frames), errors, digests | | @g9tm/syncdata/sqlite | the SQLite client store (G9SqliteSyncStore) over any G9SqliteDriver | | @g9tm/syncdata/signalr | the SignalR transport (G9SignalRSyncTransport), JSON and MessagePack with exact 64-bit values | | @g9tm/syncdata/node | node:sqlite driver (nodeSqliteDriver), Brotli through node:zlib (useNodeBrotli) | | @g9tm/syncdata/testing | fault injection over any transport (G9FaultInjectingTransport) | | @g9tm/syncdata/brotli-js | a pure-JavaScript Brotli decoder for runtimes without a native one |

Version 1.2.2 — always equal to the .NET packages' version. What maps to what, and every deliberate difference: PARITY.md.

Install

npm install @g9tm/syncdata @g9tm/signalr-supernetcore-client @microsoft/signalr @microsoft/signalr-protocol-msgpack @msgpack/msgpack

The SignalR packages are optional peers: only @g9tm/syncdata/signalr needs them.

Use (Node)

import { G9ESyncColumnType as T, G9SyncClientModelBuilder, G9SyncEngine, G9SyncParameters } from '@g9tm/syncdata';
import { G9SqliteSyncStore } from '@g9tm/syncdata/sqlite';
import { G9SignalRSyncTransport } from '@g9tm/syncdata/signalr';
import { nodeSqliteDriver, useNodeBrotli } from '@g9tm/syncdata/node';

useNodeBrotli(); // the server compresses frames with Brotli

// Tables in the order the .NET source generator emits them (entity type name), so both clients number them alike.
const model = new G9SyncClientModelBuilder()
  .table('farm.Pot', 'Pot', (t) =>
    t.key('Id').column('SiteId', T.Guid, false).column('Code', T.String, false).column('Weight', T.Decimal).index(null, false, 'SiteId'))
  .table('farm.Site', 'Site', (t) => t.key('Id').column('Name', T.String, false))
  .build();

const store = new G9SqliteSyncStore({ driver: nodeSqliteDriver(), databasePath: './device.db' });
const transport = new G9SignalRSyncTransport({ url: 'https://sync.example.com/g9sync', accessTokenFactory: () => token });
const engine = new G9SyncEngine(store, transport, model, { appVersion: '1.0.0' });

await engine.initialize();
await engine.subscribe('sites');
await engine.subscribe('site-pots', new G9SyncParameters().setGuid('SiteId', siteId));
const run = engine.run({ subscriptions: ['site-pots'] });
run.onProgress((p) => console.log(p.phase, Math.round(p.overall * 100) + '%'));
const result = await run.completion;
if (!result.isSuccess) console.error(result.error?.code, result.error?.message);

Local edits are ordinary SQL on the app's own connection: the store's triggers capture them and the next run pushes them. Commands (engine.executeCommand), the change feed (readChangeFeed), rejections, quarantine and the run reports work as on .NET (07-Client-Engine, 08-Client-Sqlite).

Lynx

On Lynx's background thread use @g9tm/syncdata-lynx: useLynxBrotli() and createLynxSyncTransport({ url }) (MessagePack over the G9 native WebSocket — release Lynx builds have none of their own), and G9LynxControls' native SQLite driver for the store. The code ships as ES2019 and uses no API the background thread lacks (no BigInt, TextEncoder, URL, btoa, globalThis timers) — the build fails otherwise.

License

MIT. The Brotli decoder in brotli-js is Google's (MIT): THIRD-PARTY-NOTICES.md.