@itslil/posthog-js
v1.418.10
Published
Selected posthog-js 1.418.10 surfaces rewritten in LilScript: a capture kernel plus surveys, error-tracking, OTLP, autocapture, and replay-core subpaths — not the full SDK.
Maintainers
Readme
@itslil/posthog-js
This is not the official posthog-js package. It ports selected [email protected] surfaces to LilScript: the original capture kernel plus five independent package lanes, including the complete autocapture utility module and a two-module session-replay core.
It is not affiliated with PostHog. The PostHog client, active replay recorder, product tours, heatmaps, web vitals, network transport, and persistence adapters are absent. The five lanes are separate subpath exports and are never folded into the root kernel comparison.
Site: yeargun.github.io/posthoglil
npm install @itslil/posthog-jsimport {
uuidv7,
normalizeFlagsResponse,
parsePostHogCookie,
endpointFor,
rateLimitContext,
isBlockedUA,
getPersonPropertiesHash,
formatQueue,
} from "@itslil/posthog-js"
const id = uuidv7()
const flags = normalizeFlagsResponse(response)
const url = endpointFor({ api_host: "https://us.i.posthog.com" }, "api", "/e/")The independent packs use their own import paths:
import { applySurveyTranslation } from "@itslil/posthog-js/surveys"
import { ErrorPropertiesBuilder, createDefaultStackParser } from "@itslil/posthog-js/error-tracking"
import { buildOtlpLogRecord, buildOtlpMetricsPayload } from "@itslil/posthog-js/otlp"
import { getElementsChainString, shouldCaptureValue } from "@itslil/posthog-js/autocapture"
import { buildNetworkRequestOptions, splitBuffer } from "@itslil/posthog-js/replay-core"Independent pack results
The official baselines are bundled directly from the untouched vendor/posthog-js git submodule at commit 9b2a1b18db64f9f6b331cbded543c5ead3ccf0cb. The submodule is read-only for this work: no original PostHog TypeScript or JavaScript is edited, copied into a rewritten baseline, or post-processed before bundling.
Each pack is measured separately against Vite 8 Oxc with mangling enabled. The figures below are direct compiler artifacts without package banners.
| Pack | Exact runtime surface | Official Oxc raw / gzip-9 / Brotli-11 | LilScript raw / gzip-9 / Brotli-11 | Result vs Oxc | | --- | --- | ---: | ---: | --- | | Autocapture utilities | 21 exports · 5/5 differential groups | 11,070 / 4,640 / 4,215 | 8,644 / 3,400 / 3,065 | 21.9% / 26.7% / 27.3% smaller | | Session replay core | 20 exports · 6/6 differential groups | 10,264 / 4,639 / 4,258 | 8,497 / 3,804 / 3,432 | 17.2% / 18.0% / 19.4% smaller | | Surveys | 21 exports · 6/6 differential groups | 6,244 / 2,515 / 2,251 | 5,302 / 2,068 / 1,765 | 15.1% / 17.8% / 21.6% smaller | | Error tracking | 26 exports · 5/5 differential groups | 14,662 / 5,700 / 5,224 | 18,835 / 6,808 / 6,200 | 28.5% / 19.4% / 18.7% larger | | OTLP logs + metrics | 14 exports · 6/6 differential groups | 6,915 / 2,790 / 2,563 | 6,826 / 2,844 / 2,594 | 1.3% smaller raw; 1.9% / 1.2% larger compressed |
Autocapture is the larger reusable win the leaf-utility kernel did not expose: 1,150 Brotli bytes (27.3%) smaller than Oxc, and 900 bytes (22.7%) smaller than three-pass Terser. Its packaged ESM is 3,159 Brotli bytes, still 25.1% below the metadata-free Oxc baseline. The replay network/buffer core is 826 Brotli bytes (19.4%) smaller than Oxc; surveys remains 21.6% smaller.
The losses remain visible. Error tracking and OTLP use the verified cost_model = raw, candidate_search = off compiler artifacts. Aggressive Brotli-scored candidates were rejected because the production differential/syntax gate caught invalid output; they are neither published nor measured. Autocapture, replay core, and surveys use their verified cost_model = brotli production artifacts. None of the LilScript rows is post-minified.
The committed differential suites cover autocapture DOM/event decisions, sensitive values, text normalization, and elements-chain serialization; replay network redaction, callback fallbacks, circular sizing, data-URI replacement, console truncation, and recursive buffer splitting; survey translation and activation semantics; every error coercer, recursive causes, exception-step byte budgets, class shapes, async frame modifiers, and browser/Node parser families; and OTLP integer boundaries, sparse/circular/deep graphs, toJSON, throwing getters, resource precedence, and log/metrics envelopes.
What is compared
Within every row group, both sides expose the same surface. The root kernel uses the pinned fixtures in official/; all five packs bundle the pinned original git submodule directly. Each official source graph is bundled with esbuild and then run through:
- Vite 8 Oxc minify, mangling on and off
- Terser, 3-pass mangling on and off, and 1-pass mangling on
- esbuild minify,
target: esnextandtarget: es2018
The LilScript rows are the compiler's own ESM: not bundled and not post-minified. Package wrappers add a license banner after compilation and are reported separately.
The published npm posthog-js IIFE still contains the client, autocapture, and replay. It is not a lane. Comparing this port to that file would be a different product against a subset.
Official Oxc / Terser / esbuild rows are one file measured three ways. Compiler-to-minifier comparisons use direct JavaScript artifacts without package metadata on either side. The root npm ESM adds a 91-byte raw license banner and is reported separately.
- Direct compiler output (
[mangle] extern_fields = true). Public names stay readable. - Packaged ESM. The verified Brotli artifact plus its license banner.
- Closed LilScript (
lilscript.closed.toml,extern_fields = false). Not published.
Measured with lilscript-codec gzip-9 / Brotli-11.
Pin: [email protected] commit 9b2a1b18db64f9f6b331cbded543c5ead3ccf0cb.
| Lane | Raw | gzip-9 | Brotli-11 | vs Oxc on that codec |
| --- | ---: | ---: | ---: | ---: |
| Official kernel | 35,193 | 9,732 | 8,561 | — |
| Official · Vite 8 Oxc mangle on | 16,123 | 6,194 | 5,622 | baseline |
| Official · Vite 8 Oxc mangle off | 22,034 | 6,951 | 6,307 | — |
| Official · Terser mangle on · 3 passes | 16,343 | 6,234 | 5,626 | — |
| Official · Terser mangle off · 3 passes | 23,298 | 7,111 | 6,428 | — |
| Official · Terser mangle on · 1 pass | 16,343 | 6,234 | 5,626 | — |
| Official · esbuild minify esnext | 16,551 | 6,355 | 5,775 | — |
| Official · esbuild minify es2018 | 17,213 | 6,598 | 5,943 | — |
| LilScript compiler · cost_model brotli | 16,683 | 6,381 | 5,606 | 0.997× Brotli |
| @itslil/posthog-js · packaged ESM | 16,774 | 6,439 | 5,749 | 1.02× Brotli |
| LilScript compiler · closed fields | 16,801 | 6,513 | 5,696 | 1.01× Brotli |
| Previous verified gzip snapshot, packaged | 16,527 | 6,415 | 5,721 | 1.04× gzip |
| Previous verified raw snapshot, packaged | 16,223 | 6,546 | 5,801 | 1.01× raw |
The current direct Brotli-scored compiler output is 16 bytes (0.3%) smaller than Oxc and 20 bytes (0.4%) smaller than Terser, with all 21 compatibility tests passing. The packaged ESM remains 127 Brotli bytes above Oxc because it includes the license banner; that packaging cost is not credited to the compiler. Raw and gzip retain their previous verified snapshots because this update deliberately did not launch another exhaustive search.
Oxc with mangling is the smallest official lane on every codec. Terser is 4 Brotli bytes behind Oxc.
Why the PostHog win is narrow
This kernel is mostly dense leaf utilities. Its public export names, protocol keys, PostHog field names, user-agent strings, and observable object shapes are fixed, leaving little structural scaffolding for LilScript to erase. Oxc and Terser already compress this kind of straight-line JavaScript close to the codec floor.
LilScript's larger wins happen when types and closed-world knowledge let it remove objects, wrappers, branches, generic machinery, or whole dependency paths. Those opportunities are deliberately scarce in this like-for-like kernel. Here the remaining advantage comes from globally scored expression shapes and binding assignments, so a 16-byte Brotli win is small but expected rather than evidence of a missing 10–30% transformation.
If LilScript is larger on a codec, that row stays. Losses are part of the comparison.
What is ported
The root export remains the capture kernel below. The five independent surfaces live at ./autocapture, ./replay-core, ./surveys, ./error-tracking, and ./otlp so their bytes never distort this table or its existing measurements.
| Module | Official source | Kept | Left out |
| --- | --- | --- | --- |
| UUID | packages/core/src/vendor/uuidv7.ts | uuidv7, uuidv4, parseUuid, uuidFromFieldsV7, uuidToHex | — |
| Flags | packages/core/src/featureFlagUtils.ts | normalize, values, payloads, minimize, allowlists | evaluation against a PostHog instance |
| Cookies | packages/core/src/cookie.ts | name, serialize, parse, consent, opt-out | document.cookie / persistence adapters |
| Router | packages/browser/src/utils/request-router.ts | hosts, region, endpointFor | RequestRouter class, rewriteRequestPath |
| Rate limit | packages/browser/src/rate-limiter.ts | token-bucket context | persistence, $$client_ingestion_warning |
| Queue | packages/browser/src/request-queue.ts | flush clamp, format, offsets, unload sort | sendBeacon, timers, transport |
| Bot | packages/core/src/utils/bot-detection.ts | DEFAULT_BLOCKED_UA_STRS, isBlockedUA | navigator / UA-CH isLikelyBot |
| Strings | packages/core/src/utils/string-utils.ts | includes, trim, strip $, distinct-id, person hash | safeJsonStringify |
| Numbers | packages/core/src/utils/number-utils.ts | clamp, remote-config bool/number, sample rate | — |
| Types | packages/core/src/utils/type-utils.ts | empty/primitive/builtin, yes/no-like, unsafe-event lists | DOM FormData / File / Event |
| JSON | packages/core/src/utils/json-utils.ts | sanitizeString | toJsonSafeValue |
| URL | packages/core/src/utils/index.ts | removeTrailingSlash, stripUrlHash | retries, timestamps |
| Bucketed limiter | packages/core/src/utils/bucketed-rate-limiter.ts | config resolve, consume | class wrapper, logger callback |
Compatibility tests compare LilScript output to the official kernel, not to the published IIFE.
npm test
npm run test:packs
npm run build
npm run build:packs
npm run measure
npm run measure:packs
npm run build:siteOxc minify uses Vite 8 and needs Node ^20.19 || >=22.12. This lab falls back to the LilScript popular-benchmark Vite if the local Node is older.
License
Apache-2.0. See LICENSE and NOTICE.md. posthog-js is copyright PostHog / Hiberly, Inc. The UUID vendor is LiosK/uuidv7.
