@trillboards/edge-core 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,107 @@
1
+ /**
2
+ * centroidTracker — ONE nearest-centroid tracker, for everything that needs one.
3
+ *
4
+ * ── WHY THIS FILE EXISTS ───────────────────────────────────────────────────
5
+ *
6
+ * Three independent implementations of "give this box the same id it had last
7
+ * frame" were in the tree, and they did not agree:
8
+ *
9
+ * edge-sensing FaceDetector.assignTrackingIds best-match, size-relative
10
+ * threshold, staleness pruning
11
+ * platform-web WebFaceDetector.assignFaceId FIRST match, fixed threshold,
12
+ * eviction by insertion order
13
+ * agent-core VehicleFlowProcessor.updateTracks (Kotlin — re-implements
14
+ * association scoring rather
15
+ * than calling the
16
+ * TrackAssociation.bestMatch
17
+ * sitting next to it)
18
+ *
19
+ * This is the TypeScript half consolidated onto the first one's algorithm, which
20
+ * is the only one of the three that is correct. The Kotlin pair is a different
21
+ * artifact with its own release cycle and is not touched here; the note above is
22
+ * so the next person does not make it a fourth.
23
+ *
24
+ * ── THE TWO DEFECTS THE WEB VERSION HAD ────────────────────────────────────
25
+ *
26
+ * 1. FIRST match, not BEST. It returned the first track inside the threshold
27
+ * rather than the closest, so with two people standing near each other the id
28
+ * that got assigned depended on Map insertion order. Two frames apart, the
29
+ * same two people could swap ids — and a swapped id is worse than a new one,
30
+ * because a new id is visibly a new person while a swap silently attributes
31
+ * one person's dwell to another.
32
+ *
33
+ * 2. Eviction by insertion order, not staleness. It dropped the OLDEST-INSERTED
34
+ * entry once the map passed 50 — which is the person who has been standing
35
+ * there longest, i.e. exactly the one a dwell metric cares about — while a
36
+ * track last seen an hour ago and re-inserted recently survived.
37
+ *
38
+ * Both are fixed by using this. The threshold also becomes size-relative
39
+ * (`max(w,h) * ratio`), which is what makes one constant work for a face filling
40
+ * the frame and a face across the room: a fixed pixel threshold is either too
41
+ * tight up close or too loose far away, and there is no value that is neither.
42
+ *
43
+ * ── WHAT THIS IS NOT ───────────────────────────────────────────────────────
44
+ *
45
+ * Not a motion model — no velocity, no prediction, no Kalman. It matches on
46
+ * position alone, so it holds an id across small movements between frames and
47
+ * loses it across an occlusion. That is sufficient for what consumes it today
48
+ * (per-frame face ids and dwell) and it is deliberately not more: a constant-
49
+ * velocity model and 512-d re-ID both already exist in `TrackAssociation.kt` and
50
+ * `TrackReId.kt`, and if this side ever needs them they should be ported, not
51
+ * reinvented here for a third time.
52
+ */
53
+ export interface TrackedBox {
54
+ x: number;
55
+ y: number;
56
+ width: number;
57
+ height: number;
58
+ }
59
+ export interface TrackRecord {
60
+ id: number;
61
+ box: TrackedBox;
62
+ firstSeen: number;
63
+ lastSeen: number;
64
+ }
65
+ export interface CentroidTrackerOptions {
66
+ /**
67
+ * Match radius as a fraction of the detection's longest side.
68
+ *
69
+ * Size-relative on purpose — see the note above on why no fixed pixel value
70
+ * works at two distances.
71
+ */
72
+ matchRatio?: number;
73
+ /** Drop a track not seen for this long. */
74
+ staleMs?: number;
75
+ /**
76
+ * Hard cap on retained tracks, evicted STALEST-FIRST.
77
+ *
78
+ * A safety valve for a pathological scene, not a working limit. Eviction order
79
+ * is the whole point: dropping the oldest-inserted entry removes the person
80
+ * who has been present longest, which is the one every dwell metric is about.
81
+ */
82
+ maxTracks?: number;
83
+ }
84
+ export declare const DEFAULT_MATCH_RATIO = 0.5;
85
+ export declare const DEFAULT_STALE_MS = 2000;
86
+ export declare const DEFAULT_MAX_TRACKS = 256;
87
+ export declare class CentroidTracker {
88
+ private readonly tracks;
89
+ private readonly matchRatio;
90
+ private readonly staleMs;
91
+ private readonly maxTracks;
92
+ private nextId;
93
+ constructor(options?: CentroidTrackerOptions);
94
+ /**
95
+ * Assign ids to one frame's detections, in order.
96
+ *
97
+ * Returns one record per input box, same length and same order, so a caller
98
+ * can zip it against its own richer per-detection data.
99
+ */
100
+ track(detections: TrackedBox[], now: number): TrackRecord[];
101
+ /** Live tracks, for callers that need to inspect state. */
102
+ active(): TrackRecord[];
103
+ reset(): void;
104
+ private pruneStale;
105
+ private enforceCap;
106
+ }
107
+ //# sourceMappingURL=centroidTracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"centroidTracker.d.ts","sourceRoot":"","sources":["../src/centroidTracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,UAAU,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AACtC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAMtC,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkC;IACzD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,MAAM,CAAK;gBAEP,OAAO,GAAE,sBAA2B;IAMhD;;;;;OAKG;IACH,KAAK,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,EAAE;IAkE3D,2DAA2D;IAC3D,MAAM,IAAI,WAAW,EAAE;IAIvB,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,UAAU;CAcnB"}
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ /**
3
+ * centroidTracker — ONE nearest-centroid tracker, for everything that needs one.
4
+ *
5
+ * ── WHY THIS FILE EXISTS ───────────────────────────────────────────────────
6
+ *
7
+ * Three independent implementations of "give this box the same id it had last
8
+ * frame" were in the tree, and they did not agree:
9
+ *
10
+ * edge-sensing FaceDetector.assignTrackingIds best-match, size-relative
11
+ * threshold, staleness pruning
12
+ * platform-web WebFaceDetector.assignFaceId FIRST match, fixed threshold,
13
+ * eviction by insertion order
14
+ * agent-core VehicleFlowProcessor.updateTracks (Kotlin — re-implements
15
+ * association scoring rather
16
+ * than calling the
17
+ * TrackAssociation.bestMatch
18
+ * sitting next to it)
19
+ *
20
+ * This is the TypeScript half consolidated onto the first one's algorithm, which
21
+ * is the only one of the three that is correct. The Kotlin pair is a different
22
+ * artifact with its own release cycle and is not touched here; the note above is
23
+ * so the next person does not make it a fourth.
24
+ *
25
+ * ── THE TWO DEFECTS THE WEB VERSION HAD ────────────────────────────────────
26
+ *
27
+ * 1. FIRST match, not BEST. It returned the first track inside the threshold
28
+ * rather than the closest, so with two people standing near each other the id
29
+ * that got assigned depended on Map insertion order. Two frames apart, the
30
+ * same two people could swap ids — and a swapped id is worse than a new one,
31
+ * because a new id is visibly a new person while a swap silently attributes
32
+ * one person's dwell to another.
33
+ *
34
+ * 2. Eviction by insertion order, not staleness. It dropped the OLDEST-INSERTED
35
+ * entry once the map passed 50 — which is the person who has been standing
36
+ * there longest, i.e. exactly the one a dwell metric cares about — while a
37
+ * track last seen an hour ago and re-inserted recently survived.
38
+ *
39
+ * Both are fixed by using this. The threshold also becomes size-relative
40
+ * (`max(w,h) * ratio`), which is what makes one constant work for a face filling
41
+ * the frame and a face across the room: a fixed pixel threshold is either too
42
+ * tight up close or too loose far away, and there is no value that is neither.
43
+ *
44
+ * ── WHAT THIS IS NOT ───────────────────────────────────────────────────────
45
+ *
46
+ * Not a motion model — no velocity, no prediction, no Kalman. It matches on
47
+ * position alone, so it holds an id across small movements between frames and
48
+ * loses it across an occlusion. That is sufficient for what consumes it today
49
+ * (per-frame face ids and dwell) and it is deliberately not more: a constant-
50
+ * velocity model and 512-d re-ID both already exist in `TrackAssociation.kt` and
51
+ * `TrackReId.kt`, and if this side ever needs them they should be ported, not
52
+ * reinvented here for a third time.
53
+ */
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ exports.CentroidTracker = exports.DEFAULT_MAX_TRACKS = exports.DEFAULT_STALE_MS = exports.DEFAULT_MATCH_RATIO = void 0;
56
+ exports.DEFAULT_MATCH_RATIO = 0.5;
57
+ exports.DEFAULT_STALE_MS = 2000;
58
+ exports.DEFAULT_MAX_TRACKS = 256;
59
+ function centre(b) {
60
+ return { cx: b.x + b.width / 2, cy: b.y + b.height / 2 };
61
+ }
62
+ class CentroidTracker {
63
+ constructor(options = {}) {
64
+ this.tracks = new Map();
65
+ this.nextId = 1;
66
+ this.matchRatio = options.matchRatio ?? exports.DEFAULT_MATCH_RATIO;
67
+ this.staleMs = options.staleMs ?? exports.DEFAULT_STALE_MS;
68
+ this.maxTracks = options.maxTracks ?? exports.DEFAULT_MAX_TRACKS;
69
+ }
70
+ /**
71
+ * Assign ids to one frame's detections, in order.
72
+ *
73
+ * Returns one record per input box, same length and same order, so a caller
74
+ * can zip it against its own richer per-detection data.
75
+ */
76
+ track(detections, now) {
77
+ this.pruneStale(now);
78
+ // FRAME-LEVEL assignment, not per-detection greedy.
79
+ //
80
+ // Taking each detection in turn and giving it its own closest free track is
81
+ // ORDER-DEPENDENT, and the order it depends on is the detector's output
82
+ // order — which nothing controls. Measured on the two-person case: tracks at
83
+ // x=100 and x=140, next frame's boxes at x=125 and x=145.
84
+ //
85
+ // per-detection greedy, boxes in order 125 -> right(15), 145 -> left(45)
86
+ // per-detection greedy, boxes reversed 145 -> right(5), 125 -> left(25)
87
+ // minimum total distance 125 -> left(25), 145 -> right(5)
88
+ //
89
+ // So the forward order produces exactly the SWAP this class exists to
90
+ // prevent, and merely reversing the input fixes it. Scoring every candidate
91
+ // pair and consuming them shortest-first is order-independent and lands on
92
+ // the minimum-distance assignment here.
93
+ //
94
+ // It is a heuristic, not the optimum: shortest-first can be beaten by
95
+ // Hungarian on contrived layouts. That trade is deliberate — a subtly wrong
96
+ // O(n^3) implementation is worse than a correct heuristic, the seam is this
97
+ // one function if it ever needs to change, and what actually mattered was
98
+ // removing the dependence on input order.
99
+ const candidates = [];
100
+ detections.forEach((det, detIndex) => {
101
+ const { cx, cy } = centre(det);
102
+ const threshold = Math.max(det.width, det.height) * this.matchRatio;
103
+ for (const [trackId, track] of this.tracks) {
104
+ const c = centre(track.box);
105
+ const dist = Math.hypot(cx - c.cx, cy - c.cy);
106
+ if (dist < threshold)
107
+ candidates.push({ detIndex, trackId, dist });
108
+ }
109
+ });
110
+ // Ties broken on trackId then detIndex so equal distances resolve the same
111
+ // way every run — Map iteration order must not decide an id.
112
+ candidates.sort((a, b) => a.dist - b.dist || a.trackId - b.trackId || a.detIndex - b.detIndex);
113
+ const assignment = new Map();
114
+ const claimed = new Set();
115
+ for (const c of candidates) {
116
+ if (claimed.has(c.trackId) || assignment.has(c.detIndex))
117
+ continue;
118
+ assignment.set(c.detIndex, c.trackId);
119
+ claimed.add(c.trackId);
120
+ }
121
+ const out = [];
122
+ detections.forEach((det, detIndex) => {
123
+ const matchedId = assignment.get(detIndex);
124
+ const id = matchedId ?? this.nextId++;
125
+ const firstSeen = matchedId !== undefined ? this.tracks.get(matchedId).firstSeen : now;
126
+ const record = {
127
+ id,
128
+ box: { x: det.x, y: det.y, width: det.width, height: det.height },
129
+ firstSeen,
130
+ lastSeen: now,
131
+ };
132
+ this.tracks.set(id, record);
133
+ out.push(record);
134
+ });
135
+ this.enforceCap(claimed);
136
+ return out;
137
+ }
138
+ /** Live tracks, for callers that need to inspect state. */
139
+ active() {
140
+ return [...this.tracks.values()];
141
+ }
142
+ reset() {
143
+ this.tracks.clear();
144
+ this.nextId = 1;
145
+ }
146
+ pruneStale(now) {
147
+ for (const [id, track] of this.tracks) {
148
+ if (now - track.lastSeen > this.staleMs)
149
+ this.tracks.delete(id);
150
+ }
151
+ }
152
+ enforceCap(claimedThisFrame) {
153
+ if (this.tracks.size <= this.maxTracks)
154
+ return;
155
+ // Stalest first, and never a track seen in THIS frame — evicting one of
156
+ // those would hand a present person a brand-new id on the next frame.
157
+ const evictable = [...this.tracks.values()]
158
+ .filter((t) => !claimedThisFrame.has(t.id))
159
+ .sort((a, b) => a.lastSeen - b.lastSeen);
160
+ let over = this.tracks.size - this.maxTracks;
161
+ for (const track of evictable) {
162
+ if (over <= 0)
163
+ break;
164
+ this.tracks.delete(track.id);
165
+ over -= 1;
166
+ }
167
+ }
168
+ }
169
+ exports.CentroidTracker = CentroidTracker;
170
+ //# sourceMappingURL=centroidTracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"centroidTracker.js","sourceRoot":"","sources":["../src/centroidTracker.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -24,4 +24,6 @@ export { ModelManager } from './ModelManager';
24
24
  export type { ModelManagerOptions, ModelInfo, ModelVersionResponse } from './ModelManager';
25
25
  export { ZKAudienceProver } from './ZKAudienceProver';
26
26
  export type { AudienceObservation, AudienceAggregate, AudienceProof, VerificationResult, } from './ZKAudienceProver';
27
+ export { CentroidTracker, DEFAULT_MATCH_RATIO, DEFAULT_STALE_MS, DEFAULT_MAX_TRACKS, } from './centroidTracker';
28
+ export type { TrackedBox, TrackRecord, CentroidTrackerOptions } from './centroidTracker';
27
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EACL,yBAAyB,EACzB,KAAK,mBAAmB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,YAAY,EACZ,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EACV,+BAA+B,EAC/B,oBAAoB,EACpB,cAAc,EACd,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,kBAAkB,GACnB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EACL,yBAAyB,EACzB,KAAK,mBAAmB,GACzB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,YAAY,EACZ,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EACV,+BAA+B,EAC/B,oBAAoB,EACpB,cAAc,EACd,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EACV,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
16
16
  };
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.ZKAudienceProver = exports.ModelManager = exports.MemoryTier = exports.MemoryAttenuationManager = exports.getAttenuationTier = exports.getShedOrder = exports.isModelActive = exports.getCapabilityMatrix = exports.computeTier = exports.classifyObservationSignal = exports.StatusServer = exports.TelemetryScheduler = exports.RecoveryLadder = exports.SubsystemHealthRegistry = exports.SqliteStore = exports.JsonFileStore = exports.SignalBuffer = exports.VASWeightConfig = exports.SocketManager = exports.SensingConfig = exports.DeviceIdentity = void 0;
18
+ exports.DEFAULT_MAX_TRACKS = exports.DEFAULT_STALE_MS = exports.DEFAULT_MATCH_RATIO = exports.CentroidTracker = exports.ZKAudienceProver = exports.ModelManager = exports.MemoryTier = exports.MemoryAttenuationManager = exports.getAttenuationTier = exports.getShedOrder = exports.isModelActive = exports.getCapabilityMatrix = exports.computeTier = exports.classifyObservationSignal = exports.StatusServer = exports.TelemetryScheduler = exports.RecoveryLadder = exports.SubsystemHealthRegistry = exports.SqliteStore = exports.JsonFileStore = exports.SignalBuffer = exports.VASWeightConfig = exports.SocketManager = exports.SensingConfig = exports.DeviceIdentity = void 0;
19
19
  __exportStar(require("./types"), exports);
20
20
  var DeviceIdentity_1 = require("./DeviceIdentity");
21
21
  Object.defineProperty(exports, "DeviceIdentity", { enumerable: true, get: function () { return DeviceIdentity_1.DeviceIdentity; } });
@@ -55,4 +55,12 @@ var ModelManager_1 = require("./ModelManager");
55
55
  Object.defineProperty(exports, "ModelManager", { enumerable: true, get: function () { return ModelManager_1.ModelManager; } });
56
56
  var ZKAudienceProver_1 = require("./ZKAudienceProver");
57
57
  Object.defineProperty(exports, "ZKAudienceProver", { enumerable: true, get: function () { return ZKAudienceProver_1.ZKAudienceProver; } });
58
+ // ONE nearest-centroid tracker. Three independent implementations existed and
59
+ // disagreed; this is the TypeScript half consolidated onto the only correct one.
60
+ // Read its header before writing a fourth.
61
+ var centroidTracker_1 = require("./centroidTracker");
62
+ Object.defineProperty(exports, "CentroidTracker", { enumerable: true, get: function () { return centroidTracker_1.CentroidTracker; } });
63
+ Object.defineProperty(exports, "DEFAULT_MATCH_RATIO", { enumerable: true, get: function () { return centroidTracker_1.DEFAULT_MATCH_RATIO; } });
64
+ Object.defineProperty(exports, "DEFAULT_STALE_MS", { enumerable: true, get: function () { return centroidTracker_1.DEFAULT_STALE_MS; } });
65
+ Object.defineProperty(exports, "DEFAULT_MAX_TRACKS", { enumerable: true, get: function () { return centroidTracker_1.DEFAULT_MAX_TRACKS; } });
58
66
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;;;;;;;;;;;;;;;AAEtF,0CAAwB;AACxB,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AAEtB,qDAAoD;AAA3C,kHAAA,eAAe,OAAA;AACxB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,qEAAoE;AAA3D,kIAAA,uBAAuB,OAAA;AAChC,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AAEvB,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAE3B,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,6EAGuC;AAFrC,wIAAA,yBAAyB,OAAA;AAG3B,iDAMyB;AALvB,4GAAA,WAAW,OAAA;AACX,oHAAA,mBAAmB,OAAA;AACnB,8GAAA,aAAa,OAAA;AACb,6GAAA,YAAY,OAAA;AACZ,mHAAA,kBAAkB,OAAA;AAEpB,uEAAsE;AAA7D,oIAAA,wBAAwB,OAAA;AAOjC,uEAAwD;AAA/C,sHAAA,UAAU,OAAA;AACnB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;;;;;;;;;;;;;;;AAEtF,0CAAwB;AACxB,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AAEtB,qDAAoD;AAA3C,kHAAA,eAAe,OAAA;AACxB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,iDAAgD;AAAvC,8GAAA,aAAa,OAAA;AACtB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,qEAAoE;AAA3D,kIAAA,uBAAuB,OAAA;AAChC,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AAEvB,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAE3B,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,6EAGuC;AAFrC,wIAAA,yBAAyB,OAAA;AAG3B,iDAMyB;AALvB,4GAAA,WAAW,OAAA;AACX,oHAAA,mBAAmB,OAAA;AACnB,8GAAA,aAAa,OAAA;AACb,6GAAA,YAAY,OAAA;AACZ,mHAAA,kBAAkB,OAAA;AAEpB,uEAAsE;AAA7D,oIAAA,wBAAwB,OAAA;AAOjC,uEAAwD;AAA/C,sHAAA,UAAU,OAAA;AACnB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AAQzB,8EAA8E;AAC9E,iFAAiF;AACjF,2CAA2C;AAC3C,qDAK2B;AAJzB,kHAAA,eAAe,OAAA;AACf,sHAAA,mBAAmB,OAAA;AACnB,mHAAA,gBAAgB,OAAA;AAChB,qHAAA,kBAAkB,OAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trillboards/edge-core",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Core identity, heartbeat, Socket.io, config, and signal buffer for Trillboards Edge AI SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",