Changelog

Every release of the Kinesis.js packages, newest first — pulled straight from the library’s published versions.

@kinesisjs/corev0.5.22026-10-08
  • #57 Fix the VERSION export, which reported the wrong version in every package.

    It was hand-written as '0.1.0' and never synced to package.json, so every release since has shipped a constant that lies — @kinesisjs/core@0.5.1 reported '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.

    VERSION is now generated from package.json into src/version.ts by scripts/sync-package-versions.mjs, which changeset:version runs right after the bump, so the release PR carries the synced constants. Each package's export test now compares VERSION against its own package.json, so the two cannot drift apart again without failing CI.

@kinesisjs/angularv0.5.22026-10-08
  • #57 Fix the VERSION export, which reported the wrong version in every package.

    It was hand-written as '0.1.0' and never synced to package.json, so every release since has shipped a constant that lies — @kinesisjs/core@0.5.1 reported '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.

    VERSION is now generated from package.json into src/version.ts by scripts/sync-package-versions.mjs, which changeset:version runs right after the bump, so the release PR carries the synced constants. Each package's export test now compares VERSION against its own package.json, so the two cannot drift apart again without failing CI.

  • Updated dependencies []:

@kinesisjs/openlayersv0.2.72026-10-08
  • #57 Fix the VERSION export, which reported the wrong version in every package.

    It was hand-written as '0.1.0' and never synced to package.json, so every release since has shipped a constant that lies — @kinesisjs/core@0.5.1 reported '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.

    VERSION is now generated from package.json into src/version.ts by scripts/sync-package-versions.mjs, which changeset:version runs right after the bump, so the release PR carries the synced constants. Each package's export test now compares VERSION against its own package.json, so the two cannot drift apart again without failing CI.

  • Updated dependencies []:

@kinesisjs/route-awarev0.1.52026-10-08
  • #57 Fix the VERSION export, which reported the wrong version in every package.

    It was hand-written as '0.1.0' and never synced to package.json, so every release since has shipped a constant that lies — @kinesisjs/core@0.5.1 reported '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.

    VERSION is now generated from package.json into src/version.ts by scripts/sync-package-versions.mjs, which changeset:version runs right after the bump, so the release PR carries the synced constants. Each package's export test now compares VERSION against its own package.json, so the two cannot drift apart again without failing CI.

  • Updated dependencies []:

@kinesisjs/leafletv0.1.42026-10-08
  • #57 Fix the VERSION export, which reported the wrong version in every package.

    It was hand-written as '0.1.0' and never synced to package.json, so every release since has shipped a constant that lies — @kinesisjs/core@0.5.1 reported '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.

    VERSION is now generated from package.json into src/version.ts by scripts/sync-package-versions.mjs, which changeset:version runs right after the bump, so the release PR carries the synced constants. Each package's export test now compares VERSION against its own package.json, so the two cannot drift apart again without failing CI.

  • Updated dependencies []:

@kinesisjs/maplibrev0.1.22026-10-08
  • #57 Fix the VERSION export, which reported the wrong version in every package.

    It was hand-written as '0.1.0' and never synced to package.json, so every release since has shipped a constant that lies — @kinesisjs/core@0.5.1 reported '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.

    VERSION is now generated from package.json into src/version.ts by scripts/sync-package-versions.mjs, which changeset:version runs right after the bump, so the release PR carries the synced constants. Each package's export test now compares VERSION against its own package.json, so the two cannot drift apart again without failing CI.

  • Updated dependencies []:

@kinesisjs/maplibrev0.1.12026-10-08
  • #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 with new Image() + decode(), which behaves the same on all of them.

    Documentation only; no change to the adapter.

@kinesisjs/route-awarev0.1.42026-07-11
  • #28 Harden the OSRM client: a malformed routing profile is now rejected before the request is issued (defense-in-depth on top of the existing encodeURIComponent escaping). The guard is syntactic ([A-Za-z0-9_-]+), so custom self-host profile names still work — only obviously-invalid values are rejected, surfacing as an INTERPOLATION_ERROR event instead of a doomed network call.
@kinesisjs/corev0.5.12026-06-15
  • #24 fix: harden against untrusted input
    • leaflet: the marker divIcon HTML now coerces numeric options (heading/speed/iconSize/…) to finite numbers and escapes interpolated icon/color values, so a malformed feed or crafted style option can no longer break out of an HTML attribute (DOM-XSS hardening).
    • core: non-finite heading/speed are dropped on ingest, so malformed feed values never reach a render adapter.
    • route-aware: the OSRM baseUrl must now be an http(s) URL and the routing profile is encodeURIComponent-escaped before being placed in the request URL.
@kinesisjs/angularv0.5.12026-06-15
@kinesisjs/openlayersv0.2.62026-06-15
@kinesisjs/leafletv0.1.32026-06-15
  • #24 fix: harden against untrusted input

    • leaflet: the marker divIcon HTML now coerces numeric options (heading/speed/iconSize/…) to finite numbers and escapes interpolated icon/color values, so a malformed feed or crafted style option can no longer break out of an HTML attribute (DOM-XSS hardening).
    • core: non-finite heading/speed are dropped on ingest, so malformed feed values never reach a render adapter.
    • route-aware: the OSRM baseUrl must now be an http(s) URL and the routing profile is encodeURIComponent-escaped before being placed in the request URL.
  • Updated dependencies []:

@kinesisjs/route-awarev0.1.32026-06-15
  • #24 fix: harden against untrusted input

    • leaflet: the marker divIcon HTML now coerces numeric options (heading/speed/iconSize/…) to finite numbers and escapes interpolated icon/color values, so a malformed feed or crafted style option can no longer break out of an HTML attribute (DOM-XSS hardening).
    • core: non-finite heading/speed are dropped on ingest, so malformed feed values never reach a render adapter.
    • route-aware: the OSRM baseUrl must now be an http(s) URL and the routing profile is encodeURIComponent-escaped before being placed in the request URL.
  • Updated dependencies []:

@kinesisjs/corev0.5.02026-06-12
  • #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. Pick bufferMs ≥ worstCaseGap to avoid the queue underrunning (which would freeze the marker).
    • Auto — playout: 'auto'. Tracker measures the last ~10 ingest gaps per vehicle and sets pace = 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: bufferMs of 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 interpolation mode. 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 PlayoutOptions and PlayoutQueueEntry to the public type surface.

@kinesisjs/angularv0.5.02026-06-12
  • #22 Expose [playout] @Input on KinesisMapDirective, mirroring the TrackerOptions.playout field that landed in @kinesisjs/core@0.5.0.

    <div kinesisMap [positions]="positions" [interpolation]="'smooth'" [playout]="'auto'"></div>
    

    Forwarded to the underlying Tracker only 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.

@kinesisjs/corev0.4.02026-06-12
  • #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 over previous2 → 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 → previous gap exceeds maxInterpolationGap, 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 catmullRomLerp as a public math helper, alongside linearLerp / haversineDistance / shortestArcDiff, for authors of custom interpolators who want the same smoothing primitive.

@kinesisjs/angularv0.4.02026-06-12
  • #20 Expose [playout] @Input on KinesisMapDirective, mirroring the TrackerOptions.playout field that landed in @kinesisjs/core@0.5.0.

    <div kinesisMap [positions]="positions" [interpolation]="'smooth'" [playout]="'auto'"></div>
    

    Forwarded to the underlying Tracker only 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.

@kinesisjs/angularv0.3.22026-06-12
@kinesisjs/angularv0.3.12026-06-12
@kinesisjs/openlayersv0.2.52026-06-12
@kinesisjs/openlayersv0.2.42026-06-12
@kinesisjs/leafletv0.1.22026-06-12
@kinesisjs/route-awarev0.1.22026-06-12
@kinesisjs/leafletv0.1.12026-06-12
@kinesisjs/route-awarev0.1.12026-06-12
@kinesisjs/corev0.3.02026-05-28
  • Add opt-in Web Worker mode (worker: true or worker: { 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: true spins 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 CustomInterpolator isn't supported in worker mode (functions can't cross the worker boundary; construction throws), updateOpacity-based fade animations degrade to snapping, and getStats() returns a snapshot refreshed every ~30 ticks.

@kinesisjs/angularv0.3.02026-05-28
  • Add opt-in Web Worker mode (worker: true or worker: { 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: true spins 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 CustomInterpolator isn't supported in worker mode (functions can't cross the worker boundary; construction throws), updateOpacity-based fade animations degrade to snapping, and getStats() returns a snapshot refreshed every ~30 ticks.

  • Updated dependencies []:

@kinesisjs/openlayersv0.2.32026-05-28
  • 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 []:

  • 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 []:

  • 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: -1 default placed it BELOW the standard OSM tile layer (zIndex 0), which then overdrew the trail. Reported by the first downstream consumer that enabled trail: { 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.zIndex default is now undefined (previously -1). The option remains available as an explicit override for existingLayer mode, 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.zIndex explicitly keep their behavior. Users on the default config get visible trails.

  • KinesisMapDirective now exposes the warningOpacity adapter option as an optional @Input, completing the v0.2.0 gap-visualization story for directive users (previously reachable only via the kinesisTracker factory).

    <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 []:

  • TrackAdapter gains an optional setVehicleState(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 stale or completed — those are followed immediately by removeVehicle(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: OpenLayersAdapter now implements the new setVehicleState hook.

    Every state change always writes a vehicleState feature property (useful for external readers / popup labels). When OpenLayersAdapterOptions.warningOpacity is configured, the adapter additionally dims the marker on warning and restores opacity 1 on active:

    new OpenLayersAdapter(map, {
      style: vehicleStyle,
      warningOpacity: 0.5, // marker fades to 50% when warning threshold passes
    });
    

    stale and completed are handled by removeVehicle and produce no opacity work here. Without warningOpacity, only the property is set — no visual change (backward compatible default).

    Pairs naturally with the v0.2.0 trail rendering: 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', default zIndex: -1) is added when enabled — trails always render below vehicle markers regardless of the vehicle layer's own zIndex. Each vehicle gets a Feature<LineString> with id trail:<vehicleId>.

    Color resolution: explicit trail.color → TrailPoint.meta.color (string) → trail.defaultColor → #3b82f6. Hex inputs (#rrggbb, #rgb) have the trail's opacity applied 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 with removeVehicle(id) and the trail layer is removed from the map in destroy().

    Backward compatible — adapter behaves identically to v0.1.x when trail is omitted or { enabled: false }.

  • Updated dependencies []:

  • KinesisMapDirective now exposes the new trail adapter 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/openlayers TrailRenderOptions for the full option surface.

  • Updated dependencies [,,]:

  • Lower AdaptiveInterpolator default minPeriodMs from 1000 to 500.

    At 1000 ms the default placed a typical 1 Hz GPS feed exactly on the boundary between the none and linear adaptive zones, and setInterval/interval(1000) jitter routinely produced sub-1000 ms periods. Each clipped tick fell into the none zone and teleported the marker — visible micro-skipping with the otherwise smooth renderLagMs buffer.

    The new 500 ms default keeps 1 Hz feeds firmly inside linear regardless of jitter. Sub-second feeds that explicitly want the none behavior can opt in:

    new Tracker({ adapter, interpolation: 'adaptive', adaptive: { minPeriodMs: 1000 } });
    

    No API change, only the default value.

  • KinesisMapDirective now exposes four advanced TrackerOptions as optional @Inputs. Previously these were only reachable via the lower-level kinesisTracker(...) factory.

    • [renderLagMs] — real-time interpolation buffer size (default 1000)
    • [adaptive] — adaptive zone thresholds object
    • [fadeAnimation] — duration / easing for the adaptive fade zone
    • [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 []:

  • Fix two critical issues discovered while building the first downstream Angular demo:

    @kinesisjs/core — real-time interpolation now actually runs. v0.1.0's Tracker.tick() always took the snap-to-current branch: at the moment a position was ingested, now == current.receivedAt, so elapsed = now − previous.receivedAt ≥ period immediately and stayed true, making interpolation unreachable outside fake-timer tests that rewind Date.now(). Added TrackerOptions.renderLagMs (default 1000 ms), the standard interpolation-buffer pattern from real-time networking: tick computes renderTime = now − renderLagMs and 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. Pass renderLagMs: 0 to restore the legacy snap-on-ingest behavior. Added two new tests covering both modes; the existing custom-interpolator tests no longer rely on vi.setSystemTime rewinding.

    @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 — so imports: [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 to ng-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's Tracker.tick() always took the snap-to-current branch: at the moment a position was ingested, now == current.receivedAt, so elapsed = now − previous.receivedAt ≥ period immediately and stayed true, making interpolation unreachable outside fake-timer tests that rewind Date.now(). Added TrackerOptions.renderLagMs (default 1000 ms), the standard interpolation-buffer pattern from real-time networking: tick computes renderTime = now − renderLagMs and 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. Pass renderLagMs: 0 to restore the legacy snap-on-ingest behavior. Added two new tests covering both modes; the existing custom-interpolator tests no longer rely on vi.setSystemTime rewinding.

    @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 — so imports: [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 to ng-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 []:

  • Fix two critical issues discovered while building the first downstream Angular demo:

    @kinesisjs/core — real-time interpolation now actually runs. v0.1.0's Tracker.tick() always took the snap-to-current branch: at the moment a position was ingested, now == current.receivedAt, so elapsed = now − previous.receivedAt ≥ period immediately and stayed true, making interpolation unreachable outside fake-timer tests that rewind Date.now(). Added TrackerOptions.renderLagMs (default 1000 ms), the standard interpolation-buffer pattern from real-time networking: tick computes renderTime = now − renderLagMs and 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. Pass renderLagMs: 0 to restore the legacy snap-on-ingest behavior. Added two new tests covering both modes; the existing custom-interpolator tests no longer rely on vi.setSystemTime rewinding.

    @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 — so imports: [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 to ng-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 []:

Initial public release.

  • Tracker orchestrator with validation, throttling, and configurable initial-position behaviour (show-immediately / wait-for-second / fade-in)
  • Interpolator modes: linear, cubic, geodesic, none
  • AdaptiveInterpolator — period-aware four-zone classifier
  • Sweeper — multi-state vehicle lifecycle (active / warning / stale / completed)
  • CustomInterpolator interface 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 TrackAdapter implementation for OpenLayers
  • managedFeatureIds option to coexist safely with non-vehicle features inside a shared VectorLayer
  • updateOpacity capability for fade animations
  • getMemoryEstimate capability for accurate stats
  • createVehicleStyle helper with Icon / Circle modes, heading rotation, and speed colour bands
  • colorForSpeed exported standalone for custom style factories

Initial public release.

  • KinesisMapDirective — standalone directive for one-line setup
  • kinesisTracker factory for programmatic use in services and route resolvers
  • Automatic DestroyRef cleanup
  • Signal<Position[]> and Observable<Position[]> both supported as the input source
  • Peer dependency: Angular 17+
  • feat(leaflet): add the Leaflet map adapter

    New @kinesisjs/leaflet package — a TrackAdapter for Leaflet, on par with @kinesisjs/openlayers: per-vehicle L.Marker lifecycle, a built-in heading-aware rotatable marker (plus static/dynamic icon factories and the createVehicleStyle helper with speed-band colouring), managedFeatureIds, updateOpacity, setVehicleState + warningOpacity gap visualisation, and optional per-vehicle trail rendering. leaflet is a peer dependency (>=1.7).

  • #12 feat(route-aware): add the road-snapping CustomInterpolator package

    New @kinesisjs/route-aware — a CustomInterpolator for @kinesisjs/core that 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:

    • OSRMInterpolator drops into new 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 on tracker.destroy().

    Defaults point at the public router.project-osrm.org demo 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-coalesced setData, 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's beforeId layer ordering. The adapter installs itself once the map style is ready, so it can be constructed and fed before load fires.

    Passes the same cross-adapter parity suite as @kinesisjs/openlayers and @kinesisjs/leaflet.