Changelog
Every release of the Kinesis.js packages, newest first — pulled straight from the library’s published versions.
#57 Fix the
VERSIONexport, which reported the wrong version in every package.It was hand-written as
'0.1.0'and never synced topackage.json, so every release since has shipped a constant that lies —@kinesisjs/core@0.5.1reported'0.1.0', and so did@kinesisjs/leaflet@0.1.3. The published type was wrong too:declare const VERSION: "0.1.0". Three packages even asserted the stale value in their tests, pinning the bug in place.VERSIONis now generated frompackage.jsonintosrc/version.tsbyscripts/sync-package-versions.mjs, whichchangeset:versionruns right after the bump, so the release PR carries the synced constants. Each package's export test now comparesVERSIONagainst its ownpackage.json, so the two cannot drift apart again without failing CI.
#57 Fix the
VERSIONexport, which reported the wrong version in every package.It was hand-written as
'0.1.0'and never synced topackage.json, so every release since has shipped a constant that lies —@kinesisjs/core@0.5.1reported'0.1.0', and so did@kinesisjs/leaflet@0.1.3. The published type was wrong too:declare const VERSION: "0.1.0". Three packages even asserted the stale value in their tests, pinning the bug in place.VERSIONis now generated frompackage.jsonintosrc/version.tsbyscripts/sync-package-versions.mjs, whichchangeset:versionruns right after the bump, so the release PR carries the synced constants. Each package's export test now comparesVERSIONagainst its ownpackage.json, so the two cannot drift apart again without failing CI.Updated dependencies []:
- @kinesisjs/core@0.5.2
- @kinesisjs/openlayers@0.2.7
#57 Fix the
VERSIONexport, which reported the wrong version in every package.It was hand-written as
'0.1.0'and never synced topackage.json, so every release since has shipped a constant that lies —@kinesisjs/core@0.5.1reported'0.1.0', and so did@kinesisjs/leaflet@0.1.3. The published type was wrong too:declare const VERSION: "0.1.0". Three packages even asserted the stale value in their tests, pinning the bug in place.VERSIONis now generated frompackage.jsonintosrc/version.tsbyscripts/sync-package-versions.mjs, whichchangeset:versionruns right after the bump, so the release PR carries the synced constants. Each package's export test now comparesVERSIONagainst its ownpackage.json, so the two cannot drift apart again without failing CI.Updated dependencies []:
- @kinesisjs/core@0.5.2
#57 Fix the
VERSIONexport, which reported the wrong version in every package.It was hand-written as
'0.1.0'and never synced topackage.json, so every release since has shipped a constant that lies —@kinesisjs/core@0.5.1reported'0.1.0', and so did@kinesisjs/leaflet@0.1.3. The published type was wrong too:declare const VERSION: "0.1.0". Three packages even asserted the stale value in their tests, pinning the bug in place.VERSIONis now generated frompackage.jsonintosrc/version.tsbyscripts/sync-package-versions.mjs, whichchangeset:versionruns right after the bump, so the release PR carries the synced constants. Each package's export test now comparesVERSIONagainst its ownpackage.json, so the two cannot drift apart again without failing CI.Updated dependencies []:
- @kinesisjs/core@0.5.2
#57 Fix the
VERSIONexport, which reported the wrong version in every package.It was hand-written as
'0.1.0'and never synced topackage.json, so every release since has shipped a constant that lies —@kinesisjs/core@0.5.1reported'0.1.0', and so did@kinesisjs/leaflet@0.1.3. The published type was wrong too:declare const VERSION: "0.1.0". Three packages even asserted the stale value in their tests, pinning the bug in place.VERSIONis now generated frompackage.jsonintosrc/version.tsbyscripts/sync-package-versions.mjs, whichchangeset:versionruns right after the bump, so the release PR carries the synced constants. Each package's export test now comparesVERSIONagainst its ownpackage.json, so the two cannot drift apart again without failing CI.Updated dependencies []:
- @kinesisjs/core@0.5.2
#57 Fix the
VERSIONexport, which reported the wrong version in every package.It was hand-written as
'0.1.0'and never synced topackage.json, so every release since has shipped a constant that lies —@kinesisjs/core@0.5.1reported'0.1.0', and so did@kinesisjs/leaflet@0.1.3. The published type was wrong too:declare const VERSION: "0.1.0". Three packages even asserted the stale value in their tests, pinning the bug in place.VERSIONis now generated frompackage.jsonintosrc/version.tsbyscripts/sync-package-versions.mjs, whichchangeset:versionruns right after the bump, so the release PR carries the synced constants. Each package's export test now comparesVERSIONagainst its ownpackage.json, so the two cannot drift apart again without failing CI.Updated dependencies []:
- @kinesisjs/core@0.5.2
#54 Fix the custom-sprite example in the docs.
It used
await map.loadImage(url), which only returns a promise from maplibre-gl v4 onward — in v3 the method is callback-based, and this package's peer range allows>=3.0.0, so the example did not hold for every supported version. It now loads the image withnew Image()+decode(), which behaves the same on all of them.Documentation only; no change to the adapter.
- #28 Harden the OSRM client: a malformed routing
profileis now rejected before the request is issued (defense-in-depth on top of the existingencodeURIComponentescaping). The guard is syntactic ([A-Za-z0-9_-]+), so custom self-host profile names still work — only obviously-invalid values are rejected, surfacing as anINTERPOLATION_ERRORevent instead of a doomed network call.
- #24 fix: harden against untrusted input
- leaflet: the marker
divIconHTML now coerces numeric options (heading/speed/iconSize/…) to finite numbers and escapes interpolatedicon/colorvalues, so a malformed feed or crafted style option can no longer break out of an HTML attribute (DOM-XSS hardening). - core: non-finite
heading/speedare dropped on ingest, so malformed feed values never reach a render adapter. - route-aware: the OSRM
baseUrlmust now be anhttp(s)URL and the routing profile isencodeURIComponent-escaped before being placed in the request URL.
- leaflet: the marker
- Updated dependencies []:
- @kinesisjs/core@0.5.1
- @kinesisjs/openlayers@0.2.6
- Updated dependencies []:
- @kinesisjs/core@0.5.1
#24 fix: harden against untrusted input
- leaflet: the marker
divIconHTML now coerces numeric options (heading/speed/iconSize/…) to finite numbers and escapes interpolatedicon/colorvalues, so a malformed feed or crafted style option can no longer break out of an HTML attribute (DOM-XSS hardening). - core: non-finite
heading/speedare dropped on ingest, so malformed feed values never reach a render adapter. - route-aware: the OSRM
baseUrlmust now be anhttp(s)URL and the routing profile isencodeURIComponent-escaped before being placed in the request URL.
- leaflet: the marker
Updated dependencies []:
- @kinesisjs/core@0.5.1
#24 fix: harden against untrusted input
- leaflet: the marker
divIconHTML now coerces numeric options (heading/speed/iconSize/…) to finite numbers and escapes interpolatedicon/colorvalues, so a malformed feed or crafted style option can no longer break out of an HTML attribute (DOM-XSS hardening). - core: non-finite
heading/speedare dropped on ingest, so malformed feed values never reach a render adapter. - route-aware: the OSRM
baseUrlmust now be anhttp(s)URL and the routing profile isencodeURIComponent-escaped before being placed in the request URL.
- leaflet: the marker
Updated dependencies []:
- @kinesisjs/core@0.5.1
#18 Add
TrackerOptions.playout— a per-vehicle queue that decouples display rate from arrival rate, so feeds with variable-period ingest (jitter, replay scrubbing, retry storms) render at a steady pace instead of speeding up and slowing down with each segment.Opt-in and non-breaking: without
playout, Tracker uses the existing classical real-time path; behaviour is byte-for-byte identical to v0.4.Two forms:
- Manual —
playout: { pace, bufferMs, maxQueue? }when you know your feed's worst-case gap. PickbufferMs ≥ worstCaseGapto avoid the queue underrunning (which would freeze the marker). - Auto —
playout: 'auto'. Tracker measures the last ~10 ingest gaps per vehicle and setspace = avg,bufferMs = max × 1.5. Behaves classically while gathering its first 5 samples, then engages playout. Each vehicle calibrates independently, so mixed fleets (1 Hz dispatch + jittery IoT) coexist cleanly.
Trade-off:
bufferMsof additional perceived latency for smooth motion. For most fleet/dispatch use cases (where "the marker is 2 s behind" beats "the marker stutters") this is the right exchange. Stable 1 Hz feeds shouldn't enable it.Composition: works on top of every
interpolationmode. Pairing with'smooth'(3-point Catmull-Rom) yields the maximum-pleasant render path for jittery feeds — smooth shapes the geometry, playout flattens the rhythm.Also adds
PlayoutOptionsandPlayoutQueueEntryto the public type surface.- Manual —
#22 Expose
[playout]@InputonKinesisMapDirective, mirroring theTrackerOptions.playoutfield that landed in@kinesisjs/core@0.5.0.<div kinesisMap [positions]="positions" [interpolation]="'smooth'" [playout]="'auto'"></div>Forwarded to the underlying
Trackeronly when set, so omitting the input keeps the classical real-time path. Use'auto'for unknown feeds (Tracker self-calibrates from the gap history) or{ pace, bufferMs, maxQueue }when you know your worst-case gap.
#16 Add
interpolation: 'smooth'— a 3-point centripetal Catmull-Rom mode for jitter and variable-period feeds.The Tracker now keeps a third historical point per vehicle (
previous2) and routes smooth-mode ticks through a cubic spline overprevious2 → previous → current, with a mirror phantom for the trailing tangent. The marker glides through each waypoint instead of kinking, which is especially visible on irregular feeds (random arrival times, dead reckoning, replay scrubbing).Opt-in and conservative:
- Default stays
'linear'. Existing apps see no behavioural change. - Until the third ingest lands the spline falls back to linear — no spurious motion from incomplete history.
- If the
previous2 → previousgap exceedsmaxInterpolationGap, that control point is dropped (stale data shouldn't warp the curve). - All sanity checks (anomalous jump, sharp turn, render-lag warm-up) and the existing custom-interpolator path are untouched.
Also exposes
catmullRomLerpas a public math helper, alongsidelinearLerp/haversineDistance/shortestArcDiff, for authors of custom interpolators who want the same smoothing primitive.- Default stays
#20 Expose
[playout]@InputonKinesisMapDirective, mirroring theTrackerOptions.playoutfield that landed in@kinesisjs/core@0.5.0.<div kinesisMap [positions]="positions" [interpolation]="'smooth'" [playout]="'auto'"></div>Forwarded to the underlying
Trackeronly when set, so omitting the input keeps the classical real-time path. Use'auto'for unknown feeds (Tracker self-calibrates from the gap history) or{ pace, bufferMs, maxQueue }when you know your worst-case gap.
- Updated dependencies []:
- @kinesisjs/core@0.5.0
- @kinesisjs/openlayers@0.2.5
- Updated dependencies []:
- @kinesisjs/core@0.4.0
- @kinesisjs/openlayers@0.2.4
- Updated dependencies []:
- @kinesisjs/core@0.5.0
- Updated dependencies []:
- @kinesisjs/core@0.4.0
- Updated dependencies []:
- @kinesisjs/core@0.5.0
- Updated dependencies []:
- @kinesisjs/core@0.5.0
- Updated dependencies []:
- @kinesisjs/core@0.4.0
- Updated dependencies []:
- @kinesisjs/core@0.4.0
Add opt-in Web Worker mode (
worker: trueorworker: { url }).The tick loop — interpolation, sanity checks, and the sweeper — can now run off the main thread inside a Web Worker, keeping the UI thread free for the actual map/DOM writes. The adapter stays on the main thread and is driven by messages the worker streams back, so existing adapters work unchanged.
worker: truespins the worker up from an inlined Blob (zero setup; adds ~2.4 KB gzip to the core bundle).worker: { url }loads the bundled worker script from a URL you control, avoiding the inline payload.
The public API is unchanged —
new Tracker({ worker: true })transparently returns a worker-backed tracker with the same surface.@kinesisjs/angular's[kinesisMap]directive exposes it via a new[worker]input.Caveats: a
CustomInterpolatorisn't supported in worker mode (functions can't cross the worker boundary; construction throws),updateOpacity-based fade animations degrade to snapping, andgetStats()returns a snapshot refreshed every ~30 ticks.
Add opt-in Web Worker mode (
worker: trueorworker: { url }).The tick loop — interpolation, sanity checks, and the sweeper — can now run off the main thread inside a Web Worker, keeping the UI thread free for the actual map/DOM writes. The adapter stays on the main thread and is driven by messages the worker streams back, so existing adapters work unchanged.
worker: truespins the worker up from an inlined Blob (zero setup; adds ~2.4 KB gzip to the core bundle).worker: { url }loads the bundled worker script from a URL you control, avoiding the inline payload.
The public API is unchanged —
new Tracker({ worker: true })transparently returns a worker-backed tracker with the same surface.@kinesisjs/angular's[kinesisMap]directive exposes it via a new[worker]input.Caveats: a
CustomInterpolatorisn't supported in worker mode (functions can't cross the worker boundary; construction throws),updateOpacity-based fade animations degrade to snapping, andgetStats()returns a snapshot refreshed every ~30 ticks.Updated dependencies []:
- @kinesisjs/core@0.3.0
- @kinesisjs/openlayers@0.2.3
- Updated dependencies []:
- @kinesisjs/core@0.3.0
Release pipeline restored — npm Trusted Publishing now verified end-to-end.
No runtime changes. This patch only re-establishes the OIDC publish flow after the v0.1.2 / v0.2.0 / v0.2.1 release failures, by ensuring all three packages have valid Trusted Publisher rules on npmjs.com that match the release workflow.
Updated dependencies []:
- @kinesisjs/core@0.2.1
Release pipeline restored — npm Trusted Publishing now verified end-to-end.
No runtime changes. This patch only re-establishes the OIDC publish flow after the v0.1.2 / v0.2.0 / v0.2.1 release failures, by ensuring all three packages have valid Trusted Publisher rules on npmjs.com that match the release workflow.
Updated dependencies []:
- @kinesisjs/core@0.2.1
- @kinesisjs/openlayers@0.2.2
Release pipeline restored — npm Trusted Publishing now verified end-to-end.
No runtime changes. This patch only re-establishes the OIDC publish flow after the v0.1.2 / v0.2.0 / v0.2.1 release failures, by ensuring all three packages have valid Trusted Publisher rules on npmjs.com that match the release workflow.
Trail layer was invisible by default in v0.2.0 — the layer's
zIndex: -1default placed it BELOW the standard OSM tile layer (zIndex 0), which then overdrew the trail. Reported by the first downstream consumer that enabledtrail: { enabled: true }without overriding zIndex.Fix:
- Trail layer is now added to the map BEFORE the adapter's vehicle layer. OpenLayers' natural render order (later-added on top) puts trails behind vehicles without needing zIndex tricks.
TrailRenderOptions.zIndexdefault is nowundefined(previously-1). The option remains available as an explicit override forexistingLayermode, where the user's own vehicle layer is already in the stack and trail-vs-vehicle order cannot be controlled by add sequence alone.
Two new tests lock in the fix:
adds the trail layer BEFORE the vehicle layer (trail renders below)honors explicit trail.zIndex when provided (existingLayer override)
No API surface change; users who were already setting
trail.zIndexexplicitly keep their behavior. Users on the default config get visible trails.
KinesisMapDirectivenow exposes thewarningOpacityadapter option as an optional@Input, completing the v0.2.0 gap-visualization story for directive users (previously reachable only via thekinesisTrackerfactory).<div kinesisMap [positions]="positions" [warningThreshold]="60000" [warningOpacity]="0.5"></div>Marker dims to 50% when a vehicle's idle exceeds
warningThreshold; restores to 1.0 on the next ingest or sweeper-detected recovery. Omit the input to keep the v0.2.0 behavior (no opacity change on warning).Updated dependencies []:
- @kinesisjs/openlayers@0.2.1
TrackAdaptergains an optionalsetVehicleState(id, state)hook. Tracker calls it whenever a vehicle transitions between lifecycle states (active ↔ warning), so adapters can render gap-visualization treatment — fading, badging, dashed trails — without having to subscribe to the event bus externally.The hook fires:
- On warning (sweeper detects idle > warningThreshold)
- On recovery to active (fresh ingest, or sweeper after slot revives)
It does NOT fire for
staleorcompleted— those are followed immediately byremoveVehicle(id), and rendering a transient terminal state isn't useful.Backward compatible: the method is optional in the interface, and adapters that don't implement it (or instances on the existing v0.1.x API) keep working unchanged.
Gap visualization:
OpenLayersAdapternow implements the newsetVehicleStatehook.Every state change always writes a
vehicleStatefeature property (useful for external readers / popup labels). WhenOpenLayersAdapterOptions.warningOpacityis configured, the adapter additionally dims the marker onwarningand restores opacity 1 onactive:new OpenLayersAdapter(map, { style: vehicleStyle, warningOpacity: 0.5, // marker fades to 50% when warning threshold passes });staleandcompletedare handled byremoveVehicleand produce no opacity work here. WithoutwarningOpacity, only the property is set — no visual change (backward compatible default).Pairs naturally with the v0.2.0
trailrendering: the dimmed marker plus the still-rendered trail tell the user "we know the last position but haven't heard back" without removing the vehicle from the map.Per-vehicle trail rendering — fading polyline behind each marker showing recent positions.
Opt in via
OpenLayersAdapter:new OpenLayersAdapter(map, { style: vehicleStyle, trail: { enabled: true, maxPoints: 60, intervalMs: 100, width: 3, opacity: 0.5 }, });A separate
VectorLayer(name: 'kinesis-trails', defaultzIndex: -1) is added when enabled — trails always render below vehicle markers regardless of the vehicle layer's own zIndex. Each vehicle gets aFeature<LineString>with idtrail:<vehicleId>.Color resolution: explicit
trail.color→TrailPoint.meta.color(string) →trail.defaultColor→#3b82f6. Hex inputs (#rrggbb,#rgb) have the trail'sopacityapplied automatically as alpha; non-hex colors (named,rgb(),rgba()) are passed through unchanged so the caller controls alpha.Throttling:
intervalMs(default 100 ms) caps how often a tick is appended to a trail. The Tracker runs at ~60 fps, so without throttling a 60-point buffer fills in one second. The default samples at ~10 Hz, giving a ~6-second visible trail.Memory: per-trail overhead ≈ 64 bytes + 16 bytes per coordinate; reflected in
getMemoryEstimate(). Trail features are torn down withremoveVehicle(id)and the trail layer is removed from the map indestroy().Backward compatible — adapter behaves identically to v0.1.x when
trailis omitted or{ enabled: false }.Updated dependencies []:
- @kinesisjs/core@0.2.0
KinesisMapDirectivenow exposes the newtrailadapter option as an optional@Input.<div kinesisMap [positions]="positions" [trail]="{ enabled: true, maxPoints: 60, intervalMs: 100, color: '#3b82f6' }" ></div>Omit the input and the directive behaves identically to v0.1.2 (no trail layer created). See
@kinesisjs/openlayersTrailRenderOptionsfor the full option surface.Updated dependencies [,,]:
- @kinesisjs/core@0.2.0
- @kinesisjs/openlayers@0.2.0
Lower
AdaptiveInterpolatordefaultminPeriodMsfrom 1000 to 500.At 1000 ms the default placed a typical 1 Hz GPS feed exactly on the boundary between the
noneandlinearadaptive zones, andsetInterval/interval(1000)jitter routinely produced sub-1000 ms periods. Each clipped tick fell into thenonezone and teleported the marker — visible micro-skipping with the otherwise smoothrenderLagMsbuffer.The new 500 ms default keeps 1 Hz feeds firmly inside
linearregardless of jitter. Sub-second feeds that explicitly want thenonebehavior can opt in:new Tracker({ adapter, interpolation: 'adaptive', adaptive: { minPeriodMs: 1000 } });No API change, only the default value.
- Updated dependencies []:
- @kinesisjs/core@0.1.2
KinesisMapDirectivenow exposes four advancedTrackerOptionsas optional@Inputs. Previously these were only reachable via the lower-levelkinesisTracker(...)factory.[renderLagMs]— real-time interpolation buffer size (default1000)[adaptive]— adaptive zone thresholds object[fadeAnimation]— duration / easing for the adaptivefadezone[initialPositionBehavior]—'show-immediately' | 'wait-for-second' | 'fade-in'
Example:
<div kinesisMap [positions]="positions" [interpolation]="'adaptive'" [renderLagMs]="800" [adaptive]="{ minPeriodMs: 200, fadeThresholdMs: 30000 }" [fadeAnimation]="{ duration: 400, easing: 'linear' }" [initialPositionBehavior]="'fade-in'" ></div>All four inputs are optional — omitting them keeps the tracker defaults.
Updated dependencies []:
- @kinesisjs/core@0.1.2
- @kinesisjs/openlayers@0.1.2
Fix two critical issues discovered while building the first downstream Angular demo:
@kinesisjs/core— real-time interpolation now actually runs. v0.1.0'sTracker.tick()always took the snap-to-current branch: at the moment a position was ingested,now == current.receivedAt, soelapsed = now − previous.receivedAt ≥ periodimmediately and stayed true, making interpolation unreachable outside fake-timer tests that rewindDate.now(). AddedTrackerOptions.renderLagMs(default 1000 ms), the standard interpolation-buffer pattern from real-time networking: tick computesrenderTime = now − renderLagMsand uses that for elapsed/ratio. With the default, a 1 Hz feed slides the marker smoothly from the previous to the current point over each second. PassrenderLagMs: 0to restore the legacy snap-on-ingest behavior. Added two new tests covering both modes; the existing custom-interpolator tests no longer rely onvi.setSystemTimerewinding.@kinesisjs/angular— built with ng-packagr, finally importable by Angular AOT consumers. v0.1.0 was bundled with tsup, which preserved raw TS decorator output (__decorate([Directive({...})], cls)). Angular AOT consumers compile against Ivy partial-Ivy metadata (ɵdir,ɵfac,ɵngDeclareDirective,ɵngDeclareClassMetadata), which tsup does not emit — soimports: [KinesisMapDirective]in any consuming standalone component failed at AOT compile time with "Component imports must be standalone components, directives, pipes, or must be NgModules." Migrated build tong-packagr(FESM2022 + partial-Ivy.d.ts); the package now compiles cleanly into Angular 17+ apps. No source-level API changes.@kinesisjs/openlayers— patch bump for monorepo cohesion only; no behavior change.
Fix two critical issues discovered while building the first downstream Angular demo:
@kinesisjs/core— real-time interpolation now actually runs. v0.1.0'sTracker.tick()always took the snap-to-current branch: at the moment a position was ingested,now == current.receivedAt, soelapsed = now − previous.receivedAt ≥ periodimmediately and stayed true, making interpolation unreachable outside fake-timer tests that rewindDate.now(). AddedTrackerOptions.renderLagMs(default 1000 ms), the standard interpolation-buffer pattern from real-time networking: tick computesrenderTime = now − renderLagMsand uses that for elapsed/ratio. With the default, a 1 Hz feed slides the marker smoothly from the previous to the current point over each second. PassrenderLagMs: 0to restore the legacy snap-on-ingest behavior. Added two new tests covering both modes; the existing custom-interpolator tests no longer rely onvi.setSystemTimerewinding.@kinesisjs/angular— built with ng-packagr, finally importable by Angular AOT consumers. v0.1.0 was bundled with tsup, which preserved raw TS decorator output (__decorate([Directive({...})], cls)). Angular AOT consumers compile against Ivy partial-Ivy metadata (ɵdir,ɵfac,ɵngDeclareDirective,ɵngDeclareClassMetadata), which tsup does not emit — soimports: [KinesisMapDirective]in any consuming standalone component failed at AOT compile time with "Component imports must be standalone components, directives, pipes, or must be NgModules." Migrated build tong-packagr(FESM2022 + partial-Ivy.d.ts); the package now compiles cleanly into Angular 17+ apps. No source-level API changes.@kinesisjs/openlayers— patch bump for monorepo cohesion only; no behavior change.Updated dependencies []:
- @kinesisjs/core@0.1.1
Fix two critical issues discovered while building the first downstream Angular demo:
@kinesisjs/core— real-time interpolation now actually runs. v0.1.0'sTracker.tick()always took the snap-to-current branch: at the moment a position was ingested,now == current.receivedAt, soelapsed = now − previous.receivedAt ≥ periodimmediately and stayed true, making interpolation unreachable outside fake-timer tests that rewindDate.now(). AddedTrackerOptions.renderLagMs(default 1000 ms), the standard interpolation-buffer pattern from real-time networking: tick computesrenderTime = now − renderLagMsand uses that for elapsed/ratio. With the default, a 1 Hz feed slides the marker smoothly from the previous to the current point over each second. PassrenderLagMs: 0to restore the legacy snap-on-ingest behavior. Added two new tests covering both modes; the existing custom-interpolator tests no longer rely onvi.setSystemTimerewinding.@kinesisjs/angular— built with ng-packagr, finally importable by Angular AOT consumers. v0.1.0 was bundled with tsup, which preserved raw TS decorator output (__decorate([Directive({...})], cls)). Angular AOT consumers compile against Ivy partial-Ivy metadata (ɵdir,ɵfac,ɵngDeclareDirective,ɵngDeclareClassMetadata), which tsup does not emit — soimports: [KinesisMapDirective]in any consuming standalone component failed at AOT compile time with "Component imports must be standalone components, directives, pipes, or must be NgModules." Migrated build tong-packagr(FESM2022 + partial-Ivy.d.ts); the package now compiles cleanly into Angular 17+ apps. No source-level API changes.@kinesisjs/openlayers— patch bump for monorepo cohesion only; no behavior change.Updated dependencies []:
- @kinesisjs/core@0.1.1
- @kinesisjs/openlayers@0.1.1
Initial public release.
Trackerorchestrator with validation, throttling, and configurable initial-position behaviour (show-immediately/wait-for-second/fade-in)Interpolatormodes:linear,cubic,geodesic,noneAdaptiveInterpolator— period-aware four-zone classifierSweeper— multi-state vehicle lifecycle (active/warning/stale/completed)CustomInterpolatorinterface with sync/async support- Tick-loop sanity checks: anomalous-jump (haversine + speed) and sharp-turn (heading)
- Event-based error handling — public methods never throw
- Performance telemetry — tick history percentiles, dropped ticks, ingest rate, memory breakdown
- Public utilities:
haversineDistance,shortestArcDiff,linearLerp - Benchmarked: 1000 vehicles ≈ 0.15 ms per tick
Initial public release.
- Full
TrackAdapterimplementation for OpenLayers managedFeatureIdsoption to coexist safely with non-vehicle features inside a sharedVectorLayerupdateOpacitycapability for fade animationsgetMemoryEstimatecapability for accurate statscreateVehicleStylehelper with Icon / Circle modes, heading rotation, and speed colour bandscolorForSpeedexported standalone for custom style factories
Initial public release.
KinesisMapDirective— standalone directive for one-line setupkinesisTrackerfactory for programmatic use in services and route resolvers- Automatic
DestroyRefcleanup Signal<Position[]>andObservable<Position[]>both supported as the input source- Peer dependency: Angular 17+
feat(leaflet): add the Leaflet map adapter
New
@kinesisjs/leafletpackage — aTrackAdapterfor Leaflet, on par with@kinesisjs/openlayers: per-vehicleL.Markerlifecycle, a built-in heading-aware rotatable marker (plus static/dynamic icon factories and thecreateVehicleStylehelper with speed-band colouring),managedFeatureIds,updateOpacity,setVehicleState+warningOpacitygap visualisation, and optional per-vehicle trail rendering.leafletis a peer dependency (>=1.7).
#12 feat(route-aware): add the road-snapping CustomInterpolator package
New
@kinesisjs/route-aware— aCustomInterpolatorfor@kinesisjs/corethat asks an OSRM server for the real road between two GPS points and walks that polyline at constant arc-length speed. Markers follow the actual street network instead of cutting straight lines across buildings.Highlights:
OSRMInterpolatordrops intonew Tracker({ interpolation: ri })— no changes to the existing engine, no map adapter coupling.- The tick is never blocked:
compute()is always synchronous.prepare()warms the cache in the background; cache misses fall back to a linear lerp this tick and snap to the road on the next. - LRU + coordinate-grid hashing → high cache hit rate (a 500-vehicle fleet on recurring routes typically generates tens of unique segment fetches).
- Coalesces concurrent fetches for the same segment hash.
- Detour guard (default
2.5×straight-line) rejects implausible routes — segment keeps using linear fallback rather than misleading the operator. dispose()clears cache + in-flight set ontracker.destroy().
Defaults point at the public
router.project-osrm.orgdemo endpoint for evaluation; production fleets should self-host (see README + PRD §22).
#45 Add
@kinesisjs/maplibre— a MapLibre GL map adapter.Renders the whole fleet from a single GeoJSON source through one symbol layer, so colour, heading rotation and opacity are data-driven expressions the GPU applies rather than per-marker DOM work. Position updates mutate features in place and are uploaded once per tick via a
queueMicrotask-coalescedsetData, so a thousand vehicles cost one upload and zero DOM nodes.The default arrow is generated at runtime as an SDF sprite, so there is no sprite sheet to host and no image request. Supports
managedFeatureIds,updateOpacity,setVehicleState+warningOpacity, trail rendering,getMemoryEstimate, and MapLibre'sbeforeIdlayer ordering. The adapter installs itself once the map style is ready, so it can be constructed and fed beforeloadfires.Passes the same cross-adapter parity suite as
@kinesisjs/openlayersand@kinesisjs/leaflet.