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

com.apptrackx.sdk

v0.2.0

Published

Mobile measurement for Unity: install attribution and in-app events. Android and WebGL. iOS is not implemented (P2.SDKU.03).

Downloads

213

Readme

AppTrackX for Unity

Install attribution and in-app events for Unity games. Android and WebGL.

iOS is not implemented — see What is missing before you plan around this package.

What this is

A C# layer over two SDKs that already exist in this repository:

| Unity target | Bridged to | How | | ------------ | ------------------- | -------------------------------------- | | Android | sdks/android | JNI → AppTrackXUnityBridge.java | | WebGL | sdks/web | apptrackx.jslib → window.AppTrackX | | iOS | nothing yet | every call throws | | Editor | nothing, on purpose | every call logs a warning | | Desktop | nothing | every call throws |

It decides nothing about attribution, holds no queue and retries nothing. Everything that can be wrong about measurement is wrong in one of those two SDKs, not here.

Install

Add the registry and the package to Packages/manifest.json:

{
  "scopedRegistries": [
    {
      "name": "AppTrackX",
      "url": "https://registry.npmjs.org",
      "scopes": ["com.apptrackx"]
    }
  ],
  "dependencies": {
    "com.apptrackx.sdk": "0.2.0"
  }
}

Unity's Package Manager speaks npm, so registry.npmjs.org works as a scoped registry directly — there is no separate service to sign up for. The scope limits it to com.apptrackx, so nothing else in your project starts resolving from there.

Pin the exact version. A range means a build can change what it measures without anybody deciding to.

Or by path, while developing

{
  "dependencies": {
    "com.apptrackx.sdk": "file:../../path/to/apptrackx/sdks/unity"
  }
}

Android also needs the native SDK

Unity compiles the Java bridge in this package, but the SDK it calls is a Kotlin library that is not in the package. It has been on Maven Central since 2026-08-17, so depend on the coordinate rather than building an AAR.

Enable Project Settings → Player → Android → Publishing Settings → Custom Main Gradle Template, then add it to the file Unity generates:

// Assets/Plugins/Android/mainTemplate.gradle
dependencies {
    implementation "com.apptrackx:apptrackx-android:0.2.0"
}

google() must be among the repositories as well — every Unity Android template has it, and it is where com.android.installreferrer lives, which is published nowhere else.

Nothing is needed for this on WebGL.

Verified on Unity 6000.3.8f1, on a physical device, 2026-08-20:

| | | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | Android build with the template above | APK built, 0 errors | | The same build with the template removed | fails — AppTrackXUnityBridge.java:10: error: package com.apptrackx.sdk does not exist | | Native SDK in the APK | 27 com.apptrackx.sdk.* classes across 4 dex files | | On device | initialized=True, a device id generated, and the apptrackx-sdk thread running | | Reaching the collector | install rejected (401): Invalid signature against deliberately fake credentials, and two events dropped on 4xx as documented |

The second row is the one that matters: this step is not optional, and skipping it is a compile failure rather than a silent no-op.

WebGL also needs the Web SDK on the page

The Unity package bridges the Web SDK; it does not contain it. The build cannot bundle it either — it is an IIFE that expects the page's own scope, where location, document.referrer and storage are. Add it to your WebGL template before the Unity loader:

<script src="/apptrackx.iife.js"></script>

If it is not there, the jslib logs once explaining exactly this and measures nothing. It does not fail the build.

Use

using AppTrackXSdk;

void Start()
{
    AppTrackX.Ready += status =>
        Debug.Log($"device {status.DeviceId}, first open: {status.IsFirstOpen}");

    AppTrackX.DeferredDeepLink += link => Router.Open(link.Url);   // Android only

#if UNITY_ANDROID
    AppTrackX.Initialize(new AppTrackXConfig
    {
        AppToken = "your-app-token",
        AppSecret = "your-app-secret",
    });
#elif UNITY_WEBGL
    AppTrackX.Initialize(new AppTrackXConfig
    {
        AppToken = "your-app-token",
        Endpoint = "https://go.apptrackx.com",
    });
#endif
}

void OnPurchase()
{
    AppTrackX.TrackEvent("purchase", 19.99m, "USD",
        new Dictionary<string, object> { { "sku", "gems_500" } });
}

Subscribe to Ready before Initialize. The Android SDK can answer from a cached install state fast enough that a handler attached afterwards misses the only time it fires.

Identify the player (optional, Android)

AppTrackX.SetUserId("player_123");   // after sign-in
AppTrackX.SetUserId(null);           // on sign-out

Your own id for the player, sent as user_id on every event tracked after the call and kept across restarts. Coin Callbacks forwards it so your server knows which player to credit; without it a callback identifies the device only. Must be 1 to 128 characters with no control characters — anything else is ignored with a warning in Logcat and the current id is kept. A blank string is ignored too, rather than clearing the id: pass null to clear. On WebGL and in the editor it logs and does nothing. Needs 0.2.0 and the 0.2.0 native SDK.

Things that will bite you

The two platforms are not the same product

Every other wrapper in this repository bridges one SDK and refuses the other platform. This one bridges two genuinely different SDKs, and the differences are real rather than cosmetic:

| | Android | WebGL | | ------------------ | --------------------- | ------------------------------- | | AppSecret | required | refused — see below | | Endpoint | optional override | required | | Environment | selects the collector | ignored — use Endpoint | | Consent | not a thing | holds every event until set | | DeferredDeepLink | fires | never fires; the web has none | | IsFirstOpen | first app open | first visit from this browser |

Initialize validates against the platform you are actually building and throws naming the field. That check runs in the editor too, so a WebGL config carrying an Android field is caught before it ships.

Never put AppSecret in a WebGL build

A WebGL build is downloaded by everyone who opens the page. A secret compiled into it is readable from the browser's network tab by someone with no tools and no skill, and it signs install reports — whoever has it can forge them against your app.

Shipping the secret inside an APK is a different and accepted bargain; the collector treats a signature as evidence of the app, not of the user. The Web SDK has no secret at all and is authenticated by Origin, which you allowlist in the app's settings (W4-40).

Initialize throws on WebGL if AppSecret is set, and ToWebJson never reads the field, so two separate mistakes would have to line up. If a build has already gone out with one, rotate the secret — refusing to start cannot recall a file that has already been served.

On WebGL, nothing is sent until consent is granted

The Web SDK's default holds every event in a queue. That is correct — it is what a consent banner needs — and it is the most confusing thing about porting a game that works on Android. The symptom is an empty dashboard with no error anywhere.

AppTrackX.SetConsent(true);   // ignored on Android, required on WebGL

or set WebConsentGranted = true if consent is already established by other means. The bridge warns once in the browser console if events are piling up undecided.

Revenue is decimal or string, never double

AppTrackX.TrackEvent("purchase", 19.99m, "USD");   // ✓
AppTrackX.TrackEvent("purchase", "19.99", "USD");  // ✓
AppTrackX.TrackEvent("purchase", 19.99, "USD");    // ✗ will not compile

The third line is the natural one to write and 19.99 as a double is 19.989999999999998. The overload exists purely so the compiler explains that rather than saying "cannot convert double to decimal".

Amounts are formatted with the invariant culture on the way out. ToString() would produce "19,99" on a device in Berlin and "19.99" on one in Boston, from identical code — and only one of those is a number to the collector.

The editor logs instead of throwing

Unsupported platforms throw; the editor does not. A shipped iOS build that silently no-ops gives you a game reporting zero installs from half its players, so throwing there costs nothing. Breaking Play mode for a developer who wired everything up correctly, on a platform where there was never any data, is not the same trade. AppTrackX.IsSupported is false in the editor.

Tests

pnpm test:unity              # 40 edit-mode tests, in batch mode
pnpm test:unity --clean      # rebuild the throwaway project first
UNITY_PATH=... pnpm test:unity

Unity cannot test a package on its own, so run-tests.mjs generates a throwaway project at unity-test-project/ (gitignored), references this package by path and runs the tests inside it. A cold run takes about a minute; the project is disposable.

These are beside pnpm verify, not inside it — the Unity toolchain is not a dependency of this repository and most machines working on it do not have one.

The WebGL bridge is different: apptrackx.jslib is plain JavaScript, so it is tested by sdks/web/src/unity-bridge.test.ts and does run inside pnpm verify. It lives there because the jslib is a consumer of the Web SDK's public surface, so changing that surface breaks a test in the package that changed it.

What is missing

  • iOS (P2.SDKU.03, P2.SDKU.04). Needs the native iOS SDK (P2.SDKI), which needs Xcode and a macOS machine this project does not have. Every call throws AppTrackXUnsupportedPlatformException.
  • .unitypackage build (P2.SDKU.07). UPM by path only for now.
  • Sample project (P2.SDKU.09).
  • Neither bridge has run on a device. The C# and the jslib are tested; the Java has been compiled by nothing. Java, C# and JavaScript share no compiler, so the agreement between the three — method names, the AppTrackXUnityBridge object name, the JSON shapes — is pinned by assertions on each side and verified by none. That needs a phone and a browser.

WebGL is not in the P2.SDKU table at all. That table describes Unity as bridging the two native SDKs, which is why the execution plan lists Unity as blocked on iOS. Bridging the Web SDK instead was asked for on 2026-08-14 and is an addition to the plan rather than a row being ticked.