@camstack/addon-post-analysis 1.2.56 → 1.2.59

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.
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-CcS9NwDl.js");
5
+ const require_dist = require("../dist-VrW5no7b.js");
6
6
  let node_fs = require("node:fs");
7
7
  node_fs = require_dist.__toESM(node_fs, 1);
8
8
  let node_path = require("node:path");
@@ -2047,6 +2047,7 @@ function fail(condition) {
2047
2047
  */
2048
2048
  function presentConditionIds(c) {
2049
2049
  const ids = [];
2050
+ if (c.systemEvent !== void 0) ids.push("systemEvent");
2050
2051
  if (c.devices !== void 0 && c.devices.length > 0) ids.push("devices");
2051
2052
  if (c.source !== void 0 && c.source !== "any") ids.push("source");
2052
2053
  if (c.classes !== void 0 && c.classes.length > 0) ids.push("classes");
@@ -2140,19 +2141,34 @@ function referencesAudioClass(classes) {
2140
2141
  if (classes === void 0) return false;
2141
2142
  return classes.some((c) => c.trim().toLowerCase().startsWith("audio-"));
2142
2143
  }
2144
+ function matchesSystemEvent(condition, subject) {
2145
+ if (!condition.kinds.includes(subject.kind)) return false;
2146
+ if (condition.deviceIds !== void 0 && (subject.deviceId === void 0 || !condition.deviceIds.includes(subject.deviceId))) return false;
2147
+ if (condition.nodeIds !== void 0 && (subject.nodeId === void 0 || !condition.nodeIds.includes(subject.nodeId))) return false;
2148
+ if (condition.packageNames !== void 0 && (subject.packageName === void 0 || !condition.packageNames.includes(subject.packageName))) return false;
2149
+ return true;
2150
+ }
2143
2151
  function evaluateRule(rule, subject, deviceState) {
2144
- if (subject.kind === "occupancy-event") {
2152
+ if (subject.kind === "system-event") {
2153
+ if (rule.delivery !== "system-event") return fail("delivery");
2154
+ const condition = rule.conditions.systemEvent;
2155
+ if (condition === void 0 || subject.systemEvent === void 0 || !matchesSystemEvent(condition, subject.systemEvent)) return fail("systemEvent");
2156
+ if (rule.conditions.occupancy !== void 0) return fail("occupancy");
2157
+ } else if (subject.kind === "occupancy-event") {
2145
2158
  if (rule.delivery !== "device-event") return fail("delivery");
2146
2159
  const occ = rule.conditions.occupancy;
2147
2160
  if (occ === void 0) return fail("occupancy");
2148
2161
  if (subject.occupancy === void 0 || !matchesOccupancy(occ, subject.occupancy)) return fail("occupancy");
2162
+ if (rule.conditions.systemEvent !== void 0) return fail("systemEvent");
2149
2163
  } else if (subject.kind === "audio-event") {
2150
2164
  if (rule.delivery !== "immediate") return fail("delivery");
2151
2165
  if (!referencesAudioClass(rule.conditions.classes)) return fail("classes");
2152
2166
  if (rule.conditions.occupancy !== void 0) return fail("occupancy");
2167
+ if (rule.conditions.systemEvent !== void 0) return fail("systemEvent");
2153
2168
  } else {
2154
2169
  if ((rule.delivery === "immediate" ? "object-event" : rule.delivery) !== subject.kind) return fail("delivery");
2155
2170
  if (rule.conditions.occupancy !== void 0) return fail("occupancy");
2171
+ if (rule.conditions.systemEvent !== void 0) return fail("systemEvent");
2156
2172
  }
2157
2173
  const c = rule.conditions;
2158
2174
  if (c.deviceState !== void 0) {
@@ -2312,7 +2328,8 @@ function matchesDeviceScope(scope, subject) {
2312
2328
  function cooldownKey(rule, subject) {
2313
2329
  const first = subject.classNames[0];
2314
2330
  const classKey = keysPerClass(rule, subject) && first !== void 0 ? `:c:${first}` : "";
2315
- return rule.throttle.scope === "rule" ? `r:${rule.id}${classKey}` : `r:${rule.id}:d:${subject.deviceId}${classKey}`;
2331
+ const scopeRef = subject.systemEvent !== void 0 ? `s:${subject.systemEvent.subject}` : `d:${subject.deviceId}`;
2332
+ return rule.throttle.scope === "rule" ? `r:${rule.id}${classKey}` : `r:${rule.id}:${scopeRef}${classKey}`;
2316
2333
  }
2317
2334
  /** True when the rule fired within its cooldown window before `now`. */
2318
2335
  function isCoolingDown(rule, lastFiredAt, now) {
@@ -2709,10 +2726,12 @@ var NcDispatcher = class {
2709
2726
  }
2710
2727
  async buildNotification(entry, target) {
2711
2728
  const subject = entry.payload.subject;
2712
- const deviceName = await this.deps.getDeviceName(subject.deviceId).catch(() => null) ?? `camera ${subject.deviceId}`;
2713
- const zoneLabels = await resolveZoneLabels(this.deps.getZoneNames, subject.deviceId, subject.zones);
2729
+ const systemEvent = subject.systemEvent;
2730
+ const displayDeviceId = systemEvent?.deviceId ?? subject.deviceId;
2731
+ const deviceName = systemEvent !== void 0 && systemEvent.deviceId === void 0 ? "system" : await this.deps.getDeviceName(displayDeviceId).catch(() => null) ?? `camera ${displayDeviceId}`;
2732
+ const zoneLabels = systemEvent !== void 0 ? [] : await resolveZoneLabels(this.deps.getZoneNames, subject.deviceId, subject.zones);
2714
2733
  const vars = buildTemplateVars(entry, deviceName, zoneLabels);
2715
- const title = renderTemplate(entry.payload.template?.title, vars) ?? entry.payload.ruleName;
2734
+ const title = renderTemplate(entry.payload.template?.title, vars) ?? systemEvent?.title ?? entry.payload.ruleName;
2716
2735
  const body = renderTemplate(entry.payload.template?.body, vars) ?? defaultBody(entry, deviceName, zoneLabels);
2717
2736
  const attachments = await this.withArtifactUrls(await this.resolveAttachments(entry));
2718
2737
  const params = pickParams(entry.payload.params);
@@ -2723,8 +2742,13 @@ var NcDispatcher = class {
2723
2742
  priority: clampPriority(paramNumber(entry.payload.params, "priority") ?? entry.payload.priority),
2724
2743
  ...params,
2725
2744
  tag: entry.ruleId,
2726
- deviceId: subject.deviceId,
2745
+ ...systemEvent !== void 0 ? systemEvent.deviceId !== void 0 ? { deviceId: systemEvent.deviceId } : {} : { deviceId: subject.deviceId },
2727
2746
  ...subject.eventId !== void 0 ? { eventId: subject.eventId } : {},
2747
+ ...systemEvent !== void 0 ? { metadata: {
2748
+ systemEventKind: systemEvent.kind,
2749
+ subject: systemEvent.subject,
2750
+ ...systemEvent.data
2751
+ } } : {},
2728
2752
  ...attachments.length > 0 ? { attachments } : {},
2729
2753
  ...actions.length > 0 ? { actions } : {}
2730
2754
  };
@@ -2951,6 +2975,7 @@ async function resolveZoneLabels(getZoneNames, deviceId, zoneIds) {
2951
2975
  function buildTemplateVars(entry, deviceName, zoneLabels) {
2952
2976
  const subject = entry.payload.subject;
2953
2977
  const occupancy = subject.occupancy;
2978
+ const systemEvent = subject.systemEvent;
2954
2979
  return {
2955
2980
  camera: deviceName,
2956
2981
  class: subject.className,
@@ -2962,7 +2987,13 @@ function buildTemplateVars(entry, deviceName, zoneLabels) {
2962
2987
  rule: entry.payload.ruleName,
2963
2988
  count: occupancy !== void 0 ? `${occupancy.count}` : "",
2964
2989
  capacity: occupancy !== void 0 ? `${occupancy.capacity}` : "",
2965
- op: occupancy !== void 0 ? occupancyOpWord(occupancy.occupied) : ""
2990
+ op: occupancy !== void 0 ? occupancyOpWord(occupancy.occupied) : "",
2991
+ eventKind: systemEvent?.kind ?? "",
2992
+ subject: systemEvent?.subject ?? "",
2993
+ node: systemEvent?.nodeId ?? "",
2994
+ package: systemEvent?.packageName ?? "",
2995
+ currentVersion: typeof systemEvent?.data["currentVersion"] === "string" ? systemEvent.data["currentVersion"] : "",
2996
+ latestVersion: typeof systemEvent?.data["latestVersion"] === "string" ? systemEvent.data["latestVersion"] : ""
2966
2997
  };
2967
2998
  }
2968
2999
  /** The human-readable edge polarity used by `{{op}}` and the default body. */
@@ -2976,6 +3007,7 @@ function renderTemplate(template, vars) {
2976
3007
  }
2977
3008
  function defaultBody(entry, deviceName, zoneLabels) {
2978
3009
  const subject = entry.payload.subject;
3010
+ if (subject.systemEvent !== void 0) return subject.systemEvent.body;
2979
3011
  const occupancy = subject.occupancy;
2980
3012
  if (occupancy !== void 0) return `${occupancy.zone ?? deviceName} ${occupancyOpWord(occupancy.occupied)} (${occupancy.count}/${occupancy.capacity})`;
2981
3013
  const label = subject.label !== void 0 ? ` (${subject.label})` : "";
@@ -3107,6 +3139,146 @@ function incomingFromOccupancyEdge(edge) {
3107
3139
  })
3108
3140
  };
3109
3141
  }
3142
+ function stringField(data, key) {
3143
+ const value = data[key];
3144
+ return typeof value === "string" && value.length > 0 ? value : void 0;
3145
+ }
3146
+ function numberField(data, key) {
3147
+ const value = data[key];
3148
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3149
+ }
3150
+ function recordField(data, key) {
3151
+ const value = data[key];
3152
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
3153
+ }
3154
+ function systemSubject(input) {
3155
+ const data = {
3156
+ ...input.event.data,
3157
+ category: input.event.category
3158
+ };
3159
+ const deviceId = input.deviceId ?? 0;
3160
+ const tags = input.deviceId !== void 0 ? { deviceId: input.deviceId } : {};
3161
+ return {
3162
+ kind: "system-event",
3163
+ origin: "system",
3164
+ subject: {
3165
+ kind: "system-event",
3166
+ recordId: input.event.id,
3167
+ deviceId,
3168
+ timestamp: input.event.timestamp.getTime(),
3169
+ classNames: [],
3170
+ zones: [],
3171
+ systemEvent: {
3172
+ kind: input.kind,
3173
+ subject: input.subject,
3174
+ title: input.title,
3175
+ body: input.body,
3176
+ ...input.deviceId !== void 0 ? { deviceId: input.deviceId } : {},
3177
+ ...input.nodeId !== void 0 ? { nodeId: input.nodeId } : {},
3178
+ ...input.packageName !== void 0 ? { packageName: input.packageName } : {},
3179
+ data
3180
+ }
3181
+ },
3182
+ log: () => ({
3183
+ tags,
3184
+ meta: {
3185
+ eventId: input.event.id,
3186
+ category: input.event.category,
3187
+ kind: input.kind,
3188
+ subject: input.subject
3189
+ }
3190
+ })
3191
+ };
3192
+ }
3193
+ /**
3194
+ * Normalize the infrastructure categories consumed by Notification Center.
3195
+ * Malformed payloads are rejected instead of guessing an identity.
3196
+ */
3197
+ function incomingFromSystemEvent(event) {
3198
+ const data = event.data;
3199
+ switch (event.category) {
3200
+ case "device.state-changed": {
3201
+ const deviceId = numberField(data, "deviceId");
3202
+ const capName = stringField(data, "capName");
3203
+ const online = recordField(data, "slice")?.["online"];
3204
+ if (deviceId === void 0 || capName !== "device-status" || typeof online !== "boolean") return null;
3205
+ return systemSubject({
3206
+ event,
3207
+ kind: online ? "camera-online" : "camera-offline",
3208
+ subject: `camera:${deviceId}`,
3209
+ title: online ? "Camera online" : "Camera offline",
3210
+ body: `Camera ${deviceId} is ${online ? "online" : "offline"}`,
3211
+ deviceId
3212
+ });
3213
+ }
3214
+ case "device.online":
3215
+ case "device.offline": {
3216
+ const deviceId = numberField(data, "deviceId");
3217
+ if (deviceId === void 0) return null;
3218
+ const online = event.category === "device.online";
3219
+ return systemSubject({
3220
+ event,
3221
+ kind: online ? "camera-online" : "camera-offline",
3222
+ subject: `camera:${deviceId}`,
3223
+ title: online ? "Camera online" : "Camera offline",
3224
+ body: `Camera ${deviceId} is ${online ? "online" : "offline"}`,
3225
+ deviceId
3226
+ });
3227
+ }
3228
+ case "stream.online":
3229
+ case "stream.offline": {
3230
+ const deviceId = numberField(data, "deviceId");
3231
+ const camStreamId = stringField(data, "camStreamId");
3232
+ if (deviceId === void 0 || camStreamId === void 0) return null;
3233
+ const online = event.category === "stream.online";
3234
+ const profile = stringField(data, "profile");
3235
+ return systemSubject({
3236
+ event,
3237
+ kind: online ? "stream-online" : "stream-offline",
3238
+ subject: `stream:${camStreamId}`,
3239
+ title: online ? "Stream online" : "Stream offline",
3240
+ body: `${profile !== void 0 ? `${profile} stream` : "Stream"} for camera ${deviceId} is ${online ? "online" : "offline"}`,
3241
+ deviceId
3242
+ });
3243
+ }
3244
+ case "agent.online":
3245
+ case "agent.offline":
3246
+ case "worker.online":
3247
+ case "worker.offline": {
3248
+ const agent = event.category.startsWith("agent.");
3249
+ const nodeId = stringField(data, agent ? "agentId" : "workerId");
3250
+ if (nodeId === void 0) return null;
3251
+ const online = event.category.endsWith(".online");
3252
+ return systemSubject({
3253
+ event,
3254
+ kind: online ? "node-online" : "node-offline",
3255
+ subject: `node:${nodeId}`,
3256
+ title: online ? "Node online" : "Node offline",
3257
+ body: `${agent ? "Agent" : "Worker"} ${nodeId} is ${online ? "online" : "offline"}`,
3258
+ nodeId
3259
+ });
3260
+ }
3261
+ case "update.available": {
3262
+ const target = stringField(data, "target");
3263
+ const packageName = stringField(data, "packageName");
3264
+ const currentVersion = stringField(data, "currentVersion");
3265
+ const latestVersion = stringField(data, "latestVersion");
3266
+ if (target !== "addon" && target !== "server" || packageName === void 0 || currentVersion === void 0 || latestVersion === void 0) return null;
3267
+ const addon = target === "addon";
3268
+ const nodeId = stringField(data, "nodeId");
3269
+ return systemSubject({
3270
+ event,
3271
+ kind: addon ? "addon-update-available" : "server-update-available",
3272
+ subject: addon ? `package:${nodeId ?? "hub"}:${packageName}` : `package:${packageName}`,
3273
+ title: addon ? "Addon update available" : "Server update available",
3274
+ body: `${packageName} ${latestVersion} is available (current ${currentVersion})${nodeId !== void 0 ? ` on node ${nodeId}` : ""}`,
3275
+ packageName,
3276
+ ...nodeId !== void 0 ? { nodeId } : {}
3277
+ });
3278
+ }
3279
+ default: return null;
3280
+ }
3281
+ }
3110
3282
  /** Re-stamp a live-producer envelope as a boot-reconcile replay of the same
3111
3283
  * record — same subject, honest origin in the log. */
3112
3284
  function asReconcile(event) {
@@ -3950,7 +4122,8 @@ var OUTBOX_RECORD_KINDS = new Set([
3950
4122
  "track-end",
3951
4123
  "device-event",
3952
4124
  "package-event",
3953
- "audio-event"
4125
+ "audio-event",
4126
+ "system-event"
3954
4127
  ]);
3955
4128
  function isOutboxRecordKind(x) {
3956
4129
  return typeof x === "string" && OUTBOX_RECORD_KINDS.has(x);
@@ -5466,7 +5639,8 @@ function outboxEntryToHistory(entry) {
5466
5639
  ...s.label !== void 0 ? { label: s.label } : {},
5467
5640
  ...s.confidence !== void 0 ? { confidence: s.confidence } : {},
5468
5641
  zones: [...s.zones],
5469
- timestamp: s.timestamp
5642
+ timestamp: s.timestamp,
5643
+ ...s.systemEvent !== void 0 ? { systemEvent: s.systemEvent } : {}
5470
5644
  }
5471
5645
  };
5472
5646
  }
@@ -5515,6 +5689,9 @@ var NotificationCenter = class NotificationCenter {
5515
5689
  /** Same rate-limit pair for the per-camera mute drop — see `reportMutedDrop`. */
5516
5690
  lastMutedReportAt = /* @__PURE__ */ new Map();
5517
5691
  mutedSuppressed = /* @__PURE__ */ new Map();
5692
+ /** Dedupes the explicit liveness category and device-status fallback when
5693
+ * both describe the same edge. */
5694
+ lastSystemLiveness = /* @__PURE__ */ new Map();
5518
5695
  /**
5519
5696
  * Serialized evaluation chain. The persist hooks are fire-and-forget for
5520
5697
  * the frame path, but two concurrent evaluations of the same device
@@ -5992,6 +6169,17 @@ var NotificationCenter = class NotificationCenter {
5992
6169
  onOccupancyEdge(edge) {
5993
6170
  this.consumeEvent(incomingFromOccupancyEdge(edge));
5994
6171
  }
6172
+ /** Event-bus infrastructure/update intake. Invalid payloads are ignored. */
6173
+ onSystemEvent(event) {
6174
+ const incoming = incomingFromSystemEvent(event);
6175
+ if (incoming === null) return;
6176
+ const system = incoming.subject.systemEvent;
6177
+ if (system !== void 0 && (system.kind.startsWith("camera-") || system.kind.startsWith("stream-") || system.kind.startsWith("node-"))) {
6178
+ if (this.lastSystemLiveness.get(system.subject) === system.kind) return;
6179
+ this.lastSystemLiveness.set(system.subject, system.kind);
6180
+ }
6181
+ this.consumeEvent(incoming);
6182
+ }
5995
6183
  buildProvider() {
5996
6184
  return {
5997
6185
  listRules: async () => ({ rules: [...this.rules.list()] }),
@@ -6384,6 +6572,7 @@ var NotificationCenter = class NotificationCenter {
6384
6572
  buildEntries(rule, subject, kind, matchedOn, userTargets = [], origin = "pipeline") {
6385
6573
  const hasEventMedia = kind === "object-event" || kind === "package-event";
6386
6574
  const isTrackScoped = kind === "object-event" || kind === "track-end";
6575
+ const isSystemEvent = kind === "system-event";
6387
6576
  const direct = new Set(rule.targets.map((t) => t.targetId));
6388
6577
  return [...rule.targets, ...userTargets.filter((t) => !direct.has(t.targetId))].filter((target) => !rule.disabledTargetIds.includes(target.targetId)).map((target) => {
6389
6578
  const payload = {
@@ -6392,15 +6581,15 @@ var NotificationCenter = class NotificationCenter {
6392
6581
  ...origin === "synthetic" ? { synthetic: true } : {},
6393
6582
  priority: rule.priority,
6394
6583
  ...rule.template !== void 0 ? { template: rule.template } : {},
6395
- media: rule.media.attach,
6396
- ...rule.media.zoneCrop === true && rule.conditions.zones !== void 0 ? { mediaZoneIds: [...rule.conditions.zones.ids] } : {},
6397
- ...rule.media.frame !== void 0 ? { mediaFrame: rule.media.frame } : {},
6398
- ...rule.media.profile !== void 0 ? { mediaProfile: rule.media.profile } : {},
6399
- ...rule.media.gif === true ? { mediaGif: true } : {},
6400
- ...rule.media.clip === true ? { mediaClip: true } : {},
6401
- ...rule.media.clipPreRollSec !== void 0 ? { mediaClipPreRollSec: rule.media.clipPreRollSec } : {},
6402
- ...rule.media?.clipSpeed !== void 0 ? { mediaClipSpeed: rule.media.clipSpeed } : {},
6403
- ...rule.media.clipPostRollSec !== void 0 ? { mediaClipPostRollSec: rule.media.clipPostRollSec } : {},
6584
+ media: isSystemEvent ? "none" : rule.media.attach,
6585
+ ...!isSystemEvent && rule.media.zoneCrop === true && rule.conditions.zones !== void 0 ? { mediaZoneIds: [...rule.conditions.zones.ids] } : {},
6586
+ ...!isSystemEvent && rule.media.frame !== void 0 ? { mediaFrame: rule.media.frame } : {},
6587
+ ...!isSystemEvent && rule.media.profile !== void 0 ? { mediaProfile: rule.media.profile } : {},
6588
+ ...!isSystemEvent && rule.media.gif === true ? { mediaGif: true } : {},
6589
+ ...!isSystemEvent && rule.media.clip === true ? { mediaClip: true } : {},
6590
+ ...!isSystemEvent && rule.media.clipPreRollSec !== void 0 ? { mediaClipPreRollSec: rule.media.clipPreRollSec } : {},
6591
+ ...!isSystemEvent && rule.media?.clipSpeed !== void 0 ? { mediaClipSpeed: rule.media.clipSpeed } : {},
6592
+ ...!isSystemEvent && rule.media.clipPostRollSec !== void 0 ? { mediaClipPostRollSec: rule.media.clipPostRollSec } : {},
6404
6593
  ...matchedOn !== void 0 && matchedOn.length > 0 ? { matchedOn } : {},
6405
6594
  ...target.params !== void 0 ? { params: target.params } : {},
6406
6595
  subject: {
@@ -6412,7 +6601,8 @@ var NotificationCenter = class NotificationCenter {
6412
6601
  ...subject.confidence !== void 0 ? { confidence: subject.confidence } : {},
6413
6602
  zones: subject.zones,
6414
6603
  timestamp: subject.timestamp,
6415
- ...subject.occupancy !== void 0 ? { occupancy: frozenOccupancy(rule, subject.occupancy) } : {}
6604
+ ...subject.occupancy !== void 0 ? { occupancy: frozenOccupancy(rule, subject.occupancy) } : {},
6605
+ ...subject.systemEvent !== void 0 ? { systemEvent: subject.systemEvent } : {}
6416
6606
  }
6417
6607
  };
6418
6608
  return {
@@ -6489,7 +6679,7 @@ var NotificationCenter = class NotificationCenter {
6489
6679
  for (const entry of entries) {
6490
6680
  const rule = this.rules.get(entry.ruleId);
6491
6681
  if (rule === null) continue;
6492
- const key = rule.throttle.scope === "rule" ? `r:${rule.id}` : `r:${rule.id}:d:${entry.deviceId}`;
6682
+ const key = rule.throttle.scope === "rule" ? `r:${rule.id}` : entry.payload.subject.systemEvent !== void 0 ? `r:${rule.id}:s:${entry.payload.subject.systemEvent.subject}` : `r:${rule.id}:d:${entry.deviceId}`;
6493
6683
  const prev = this.lastFiredAt.get(key) ?? 0;
6494
6684
  if (entry.createdAt > prev) this.lastFiredAt.set(key, entry.createdAt);
6495
6685
  }
@@ -12544,26 +12734,6 @@ function wideCentralSquareLayout(bbox, frame) {
12544
12734
  frameOriginX
12545
12735
  };
12546
12736
  }
12547
- //#endregion
12548
- //#region src/shared/frame/subject-crop-variants.ts
12549
- /**
12550
- * Best-shot subject-crop variants (best-crop fast-load, 2026-07-21; 16:9
12551
- * central-square reframe, 2026-07-22).
12552
- *
12553
- * ONE native fetch (the 16:9 central-square WINDOW — see
12554
- * {@link wide-central-square-crop}) backs both persisted variants:
12555
- * - `thumbnail` — the native uncapped 16:9 window JPEG (subject in the
12556
- * central square, lateral scene context, never upscaled).
12557
- * - `thumbnailSmall` — the SAME window downscaled to {@link WIDE_THUMBNAIL_SMALL_MAX_WIDTH}
12558
- * long side (854×480), the reel / lists / grid fast-load
12559
- * representative.
12560
- *
12561
- * The small variant is DERIVED from the already-composed native window — never a
12562
- * second native round-trip and NEVER upscaled: if the window is already ≤ 854 on
12563
- * its long side it is stored AS-IS (byte-identical). The window is composed from
12564
- * the in-frame slab plus lateral letterbox padding for the (rare) part of the
12565
- * 16:9 window that falls outside the frame at a horizontal edge.
12566
- */
12567
12737
  /** Gaussian sigma for the blurred-scene letterbox fill. */
12568
12738
  var LETTERBOX_BLUR_SIGMA = 25;
12569
12739
  /** Brightness factor for the blurred fill — dimmed so the real slab reads as
@@ -12609,15 +12779,12 @@ async function composeWideCentralSquareThumbnail(slab, layout) {
12609
12779
  }
12610
12780
  /**
12611
12781
  * Derive the `thumbnailSmall` JPEG from the composed native 16:9 window.
12612
- * Downscales to at most {@link WIDE_THUMBNAIL_SMALL_MAX_WIDTH} (854) on the long
12613
- * side (aspect preserved → ~854×480). If the window's long side is already ≤ the
12614
- * cap, the ORIGINAL buffer is returned unchanged (store as-is, never upscale).
12782
+ * Downscales to at most {@link WIDE_THUMBNAIL_SMALL_MAX_WIDTH} (480) on the long
12783
+ * side (aspect preserved → 480×270) and re-encodes at representative quality.
12784
+ * `withoutEnlargement` preserves smaller sources.
12615
12785
  */
12616
12786
  async function deriveThumbnailSmall(nativeWideJpeg) {
12617
- const meta = await (0, sharp.default)(nativeWideJpeg).metadata();
12618
- const longSide = Math.max(meta.width ?? 0, meta.height ?? 0);
12619
- if (longSide > 0 && longSide <= 854) return nativeWideJpeg;
12620
- return (0, sharp.default)(nativeWideJpeg).resize(854, null, { withoutEnlargement: true }).jpeg({ quality: 88 }).toBuffer();
12787
+ return (0, sharp.default)(nativeWideJpeg).resize(480, null, { withoutEnlargement: true }).jpeg({ quality: 75 }).toBuffer();
12621
12788
  }
12622
12789
  //#endregion
12623
12790
  //#region src/shared/frame/keyframe-wide-thumbnail.ts
@@ -14871,6 +15038,7 @@ function createEventMediaHandler(deps) {
14871
15038
  }
14872
15039
  const variant = parseEventMediaVariant(query);
14873
15040
  const preferKind = parseEventMediaKind(query);
15041
+ const resolveStartedAt = performance.now();
14874
15042
  let media = null;
14875
15043
  try {
14876
15044
  media = await deps.getMedia(eventId, variant ?? void 0, preferKind ?? void 0);
@@ -14878,21 +15046,24 @@ function createEventMediaHandler(deps) {
14878
15046
  const body = "Internal server error";
14879
15047
  res.writeHead(500, {
14880
15048
  "content-type": "text/plain",
14881
- "content-length": String(Buffer.byteLength(body))
15049
+ "content-length": String(Buffer.byteLength(body)),
15050
+ "server-timing": `resolve;dur=${(performance.now() - resolveStartedAt).toFixed(1)}`
14882
15051
  });
14883
15052
  if (req.method !== "HEAD") res.end(body);
14884
15053
  else res.end();
14885
15054
  return;
14886
15055
  }
14887
15056
  if (media === null) {
14888
- res.writeHead(404).end();
15057
+ res.writeHead(404, { "server-timing": `resolve;dur=${(performance.now() - resolveStartedAt).toFixed(1)}` }).end();
14889
15058
  return;
14890
15059
  }
14891
15060
  const etag = "\"" + media.key + "\"";
15061
+ const serverTiming = `resolve;dur=${(performance.now() - resolveStartedAt).toFixed(1)}`;
14892
15062
  if (req.headers["if-none-match"] === etag) {
14893
15063
  res.writeHead(304, {
14894
15064
  etag,
14895
- "cache-control": CACHE_CONTROL
15065
+ "cache-control": CACHE_CONTROL,
15066
+ "server-timing": serverTiming
14896
15067
  }).end();
14897
15068
  return;
14898
15069
  }
@@ -14900,7 +15071,8 @@ function createEventMediaHandler(deps) {
14900
15071
  "content-type": "image/jpeg",
14901
15072
  "cache-control": CACHE_CONTROL,
14902
15073
  etag,
14903
- "content-length": String(media.bytes.byteLength)
15074
+ "content-length": String(media.bytes.byteLength),
15075
+ "server-timing": serverTiming
14904
15076
  });
14905
15077
  if (req.method === "HEAD") res.end();
14906
15078
  else res.end(Buffer.from(media.bytes));
@@ -26535,6 +26707,18 @@ function wireSubscriptions(deps) {
26535
26707
  if (nodes === null) return;
26536
26708
  deps.onClusterTopology(nodes);
26537
26709
  }));
26710
+ unsubs.push(deps.eventBus.subscribe({ category: [
26711
+ require_dist.EventCategory.DeviceOnline,
26712
+ require_dist.EventCategory.DeviceOffline,
26713
+ require_dist.EventCategory.DeviceStateChanged,
26714
+ require_dist.EventCategory.StreamOnline,
26715
+ require_dist.EventCategory.StreamOffline,
26716
+ require_dist.EventCategory.AgentOnline,
26717
+ require_dist.EventCategory.AgentOffline,
26718
+ require_dist.EventCategory.WorkerOnline,
26719
+ require_dist.EventCategory.WorkerOffline,
26720
+ require_dist.EventCategory.UpdateAvailable
26721
+ ] }, (ev) => deps.onSystemEvent(ev)));
26538
26722
  }
26539
26723
  unsubs.push(deps.eventBus.subscribe({ category: require_dist.EventCategory.DeviceBindingsChanged }, (ev) => {
26540
26724
  deps.onBindingsChanged(ev.data);
@@ -27627,9 +27811,9 @@ var EventMediaDispatcher = class {
27627
27811
  * the runner's retained native surface with NO `maxWidth` (uncapped TRUE
27628
27812
  * native). It is composed into the full 16:9 window (lateral letterbox only for
27629
27813
  * the part outside the frame) → the `thumbnail`. The `thumbnailSmall` is
27630
- * DERIVED by downscaling that SAME window to the 854 long-side cap (854×480) —
27814
+ * DERIVED by downscaling that SAME window to the 480 long-side cap (480×270) —
27631
27815
  * never a second `getNativeCropJpeg`, never upscaled ({@link deriveThumbnailSmall}
27632
- * returns the window as-is when it is already ≤ 854).
27816
+ * never upscales a smaller window).
27633
27817
  *
27634
27818
  * Both variants share the SAME framing + central-square containment guarantee:
27635
27819
  * wide consumers use the 16:9 image as-is (subject centered, no cut); square
@@ -30888,6 +31072,7 @@ var MOTION_EVENT_HEARTBEAT_MS = 5e3;
30888
31072
  var CLEAN_MEDIA_KINDS = [
30889
31073
  "crop",
30890
31074
  "thumbnail",
31075
+ "thumbnailSmall",
30891
31076
  "fullFrame",
30892
31077
  "keyFrame",
30893
31078
  "keyFrameSmall"
@@ -31587,7 +31772,8 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
31587
31772
  onSensorStateChanged: (data, timestampMs) => this.handleSensorStateChanged(data, timestampMs),
31588
31773
  onBindingsChanged: (data) => this.handleBindingsChangedEvent(data),
31589
31774
  onDeviceUnregistered: (deviceId) => this.handleDeviceUnregisteredEvent(deviceId),
31590
- onClusterTopology: (nodes) => this.nodeLiveness.applySnapshot(nodes, Date.now())
31775
+ onClusterTopology: (nodes) => this.nodeLiveness.applySnapshot(nodes, Date.now()),
31776
+ onSystemEvent: (event) => this.notificationCenter?.onSystemEvent(event)
31591
31777
  });
31592
31778
  if (this.isPostProcessingNode) await this.startEmbeddingDispatcher(logger, transport);
31593
31779
  this.startSweepTimers();
@@ -36226,6 +36412,7 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
36226
36412
  * failure the full blob is served (a thumb must never 500 / blank a tile).
36227
36413
  */
36228
36414
  async applyThumbVariant(media, variant) {
36415
+ if (media.key.includes(":thumbnailSmall") && media.bytes.byteLength <= 64 * 1024) return media;
36229
36416
  try {
36230
36417
  return {
36231
36418
  bytes: await makeSquareThumb(media.bytes, variant.size),
@@ -36244,7 +36431,13 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
36244
36431
  const eventFiles = await (this.mediaStore?.listByOwner("event", id) ?? Promise.resolve([]));
36245
36432
  if (preferKind !== void 0 && preferKind.length > 0) {
36246
36433
  const trackFiles = await (this.mediaStore?.listByOwner("track", id) ?? Promise.resolve([]));
36247
- const chosen = resolveCropForcedMedia([...eventFiles, ...trackFiles], (files) => pickCleanMedia(files, preferKind));
36434
+ const ownerTrackId = trackFiles.length === 0 ? await (this.eventStore?.getTrackIdForEvent(id) ?? Promise.resolve(null)) : null;
36435
+ const owningTrackFiles = ownerTrackId === null ? [] : await (this.mediaStore?.listByOwner("track", ownerTrackId) ?? Promise.resolve([]));
36436
+ const chosen = resolveCropForcedMedia([
36437
+ ...eventFiles,
36438
+ ...trackFiles,
36439
+ ...owningTrackFiles
36440
+ ], (files) => pickCleanMedia(files, preferKind));
36248
36441
  if (!chosen) return null;
36249
36442
  return {
36250
36443
  bytes: Buffer.from(chosen.base64, "base64"),