@camstack/addon-post-analysis 1.2.302 → 1.2.304
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.
- package/dist/{clip-model-registry-CQIBimj5.js → clip-model-registry--qJe8fTH.js} +60 -16
- package/dist/{clip-model-registry-BtY4fS3d.mjs → clip-model-registry-DBKCbXPJ.mjs} +55 -17
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +1 -1
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DVMLLink.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-sLPWG5v8.mjs} +1 -1
- package/dist/pipeline-analytics/{_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-m9iqcykg.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CMq-UvYj.mjs} +1 -1
- package/dist/pipeline-analytics/{hostInit-DRjMNOyG.mjs → hostInit-D23-Tde_.mjs} +1 -1
- package/dist/pipeline-analytics/index.js +194 -23
- package/dist/pipeline-analytics/index.mjs +194 -23
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_clip_model_registry = require("../clip-model-registry
|
|
5
|
+
const require_clip_model_registry = require("../clip-model-registry--qJe8fTH.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_clip_model_registry.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -14209,11 +14209,26 @@ function sortedSystemEvent(condition) {
|
|
|
14209
14209
|
...packageNames !== void 0 ? { packageNames: packageNames.toSorted(byValue) } : {}
|
|
14210
14210
|
};
|
|
14211
14211
|
}
|
|
14212
|
+
/**
|
|
14213
|
+
* `zones` with its redundant quantifier dropped: `match: 'any'` says exactly
|
|
14214
|
+
* what an absent `match` says (D690 — the editors write `{ ids }`, the store
|
|
14215
|
+
* still holds `match: 'any'` on every rule written before). `'all'` is kept:
|
|
14216
|
+
* it IS a different rule.
|
|
14217
|
+
*/
|
|
14218
|
+
function canonicalZones(conditions) {
|
|
14219
|
+
const zones = conditions.zones;
|
|
14220
|
+
if (zones?.match !== "any") return conditions;
|
|
14221
|
+
return {
|
|
14222
|
+
...conditions,
|
|
14223
|
+
zones: { ids: zones.ids }
|
|
14224
|
+
};
|
|
14225
|
+
}
|
|
14212
14226
|
/** The canonical form two conditions are compared in. */
|
|
14213
14227
|
function conditionsFingerprint(conditions) {
|
|
14214
|
-
const
|
|
14215
|
-
|
|
14216
|
-
|
|
14228
|
+
const canonical = canonicalZones(conditions);
|
|
14229
|
+
const { systemEvent } = canonical;
|
|
14230
|
+
return require_clip_model_registry.canonicalHash(systemEvent === void 0 ? canonical : {
|
|
14231
|
+
...canonical,
|
|
14217
14232
|
systemEvent: sortedSystemEvent(systemEvent)
|
|
14218
14233
|
});
|
|
14219
14234
|
}
|
|
@@ -14222,7 +14237,8 @@ function conditionsFingerprint(conditions) {
|
|
|
14222
14237
|
* (the normalisation the store applies on every write) and are compared
|
|
14223
14238
|
* canonically: key order is irrelevant, an `undefined` key equals an absent
|
|
14224
14239
|
* one, and the system-event `kinds` and id-list filters are compared as SETS.
|
|
14225
|
-
* Every other array keeps its order
|
|
14240
|
+
* Every other array keeps its order, and `zones.match: 'any'` equals an absent
|
|
14241
|
+
* `match`. A side that does not parse is treated as
|
|
14226
14242
|
* a change, so it is judged rather than waved through.
|
|
14227
14243
|
*/
|
|
14228
14244
|
function sameConditions(a, b) {
|
|
@@ -14267,7 +14283,7 @@ function assertPatchKeepsFamily(existing, patch) {
|
|
|
14267
14283
|
* the offending kind or filter). Then rethrow. A refused save is dropped work,
|
|
14268
14284
|
* and dropped work is never silent. Any other error passes through unlogged.
|
|
14269
14285
|
*/
|
|
14270
|
-
function logRefusal$
|
|
14286
|
+
function logRefusal$4(check, ctx) {
|
|
14271
14287
|
try {
|
|
14272
14288
|
check();
|
|
14273
14289
|
} catch (err) {
|
|
@@ -14283,11 +14299,11 @@ function logRefusal$3(check, ctx) {
|
|
|
14283
14299
|
}
|
|
14284
14300
|
/** {@link assertRuleFamily}, logged. The create path of both routes. */
|
|
14285
14301
|
function guardRuleFamily(rule, ctx) {
|
|
14286
|
-
logRefusal$
|
|
14302
|
+
logRefusal$4(() => assertRuleFamily(rule), ctx);
|
|
14287
14303
|
}
|
|
14288
14304
|
/** {@link assertPatchKeepsFamily}, logged. The update path of both routes. */
|
|
14289
14305
|
function guardPatchFamily(existing, patch, ctx) {
|
|
14290
|
-
logRefusal$
|
|
14306
|
+
logRefusal$4(() => assertPatchKeepsFamily(existing, patch), ctx);
|
|
14291
14307
|
}
|
|
14292
14308
|
//#endregion
|
|
14293
14309
|
//#region src/notification-center/device-scope-guard.ts
|
|
@@ -14312,15 +14328,15 @@ function assertDevicesExcludeDelivery(rule) {
|
|
|
14312
14328
|
throw new NcDevicesExcludeRefusal(rule.delivery, excluded);
|
|
14313
14329
|
}
|
|
14314
14330
|
/** Run the check; a refusal is dropped work, so it is logged before it is rethrown. */
|
|
14315
|
-
function logRefusal$
|
|
14331
|
+
function logRefusal$3(check, ctx) {
|
|
14316
14332
|
try {
|
|
14317
14333
|
check();
|
|
14318
14334
|
} catch (err) {
|
|
14319
|
-
if (err instanceof NcDevicesExcludeRefusal) warnRefusal$
|
|
14335
|
+
if (err instanceof NcDevicesExcludeRefusal) warnRefusal$2(ctx.logger, err, ctx);
|
|
14320
14336
|
throw err;
|
|
14321
14337
|
}
|
|
14322
14338
|
}
|
|
14323
|
-
function warnRefusal$
|
|
14339
|
+
function warnRefusal$2(logger, err, ctx) {
|
|
14324
14340
|
logger.warn("nc rule refused: devicesExclude on a system rule", { meta: {
|
|
14325
14341
|
...ctx.ruleId !== void 0 ? { ruleId: ctx.ruleId } : {},
|
|
14326
14342
|
by: ctx.by,
|
|
@@ -14330,7 +14346,7 @@ function warnRefusal$1(logger, err, ctx) {
|
|
|
14330
14346
|
}
|
|
14331
14347
|
/** The create path of both routes. */
|
|
14332
14348
|
function guardRuleDevicesExclude(rule, ctx) {
|
|
14333
|
-
logRefusal$
|
|
14349
|
+
logRefusal$3(() => assertDevicesExcludeDelivery(rule), ctx);
|
|
14334
14350
|
}
|
|
14335
14351
|
/**
|
|
14336
14352
|
* The update path of both routes: the MERGED rule is judged, and only when the
|
|
@@ -14340,7 +14356,7 @@ function guardPatchDevicesExclude(existing, patch, ctx) {
|
|
|
14340
14356
|
if (existing === null) return;
|
|
14341
14357
|
const merged = mergedFamilySubject(existing, patch);
|
|
14342
14358
|
if (merged === null) return;
|
|
14343
|
-
logRefusal$
|
|
14359
|
+
logRefusal$3(() => assertDevicesExcludeDelivery(merged), ctx);
|
|
14344
14360
|
}
|
|
14345
14361
|
//#endregion
|
|
14346
14362
|
//#region src/notification-center/rule-summary-guard.ts
|
|
@@ -14398,7 +14414,7 @@ function mergedDeliverySubject(existing, patch, clear) {
|
|
|
14398
14414
|
};
|
|
14399
14415
|
return JUDGED_KEYS.some((key) => require_clip_model_registry.canonicalHash(merged[key] ?? null) !== require_clip_model_registry.canonicalHash(existing[key] ?? null)) ? merged : null;
|
|
14400
14416
|
}
|
|
14401
|
-
function logRefusal$
|
|
14417
|
+
function logRefusal$2(check, ctx) {
|
|
14402
14418
|
try {
|
|
14403
14419
|
check();
|
|
14404
14420
|
} catch (err) {
|
|
@@ -14412,14 +14428,14 @@ function logRefusal$1(check, ctx) {
|
|
|
14412
14428
|
}
|
|
14413
14429
|
/** The create path of both routes. */
|
|
14414
14430
|
function guardRuleSystemEventDelivery(rule, ctx) {
|
|
14415
|
-
logRefusal$
|
|
14431
|
+
logRefusal$2(() => assertSystemEventDelivery(rule), ctx);
|
|
14416
14432
|
}
|
|
14417
14433
|
/** The update path of both routes: the MERGED rule, when the patch changes what is judged. */
|
|
14418
14434
|
function guardPatchSystemEventDelivery(existing, patch, clear, ctx) {
|
|
14419
14435
|
if (existing === null) return;
|
|
14420
14436
|
const merged = mergedDeliverySubject(existing, patch, clear);
|
|
14421
14437
|
if (merged === null) return;
|
|
14422
|
-
logRefusal$
|
|
14438
|
+
logRefusal$2(() => assertSystemEventDelivery(merged), ctx);
|
|
14423
14439
|
}
|
|
14424
14440
|
//#endregion
|
|
14425
14441
|
//#region src/notification-center/sensor-transition-guard.ts
|
|
@@ -14441,15 +14457,15 @@ function assertSensorTransitionDelivery(rule) {
|
|
|
14441
14457
|
throw new NcSensorTransitionRefusal(rule.delivery, transition);
|
|
14442
14458
|
}
|
|
14443
14459
|
/** Run the check; a refusal is dropped work, so it is logged before it is rethrown. */
|
|
14444
|
-
function logRefusal(check, ctx) {
|
|
14460
|
+
function logRefusal$1(check, ctx) {
|
|
14445
14461
|
try {
|
|
14446
14462
|
check();
|
|
14447
14463
|
} catch (err) {
|
|
14448
|
-
if (err instanceof NcSensorTransitionRefusal) warnRefusal(ctx.logger, err, ctx);
|
|
14464
|
+
if (err instanceof NcSensorTransitionRefusal) warnRefusal$1(ctx.logger, err, ctx);
|
|
14449
14465
|
throw err;
|
|
14450
14466
|
}
|
|
14451
14467
|
}
|
|
14452
|
-
function warnRefusal(logger, err, ctx) {
|
|
14468
|
+
function warnRefusal$1(logger, err, ctx) {
|
|
14453
14469
|
logger.warn("nc rule refused: sensorTransition off a device-event rule", { meta: {
|
|
14454
14470
|
...ctx.ruleId !== void 0 ? { ruleId: ctx.ruleId } : {},
|
|
14455
14471
|
by: ctx.by,
|
|
@@ -14459,7 +14475,7 @@ function warnRefusal(logger, err, ctx) {
|
|
|
14459
14475
|
}
|
|
14460
14476
|
/** The create path of both routes. */
|
|
14461
14477
|
function guardRuleSensorTransition(rule, ctx) {
|
|
14462
|
-
logRefusal(() => assertSensorTransitionDelivery(rule), ctx);
|
|
14478
|
+
logRefusal$1(() => assertSensorTransitionDelivery(rule), ctx);
|
|
14463
14479
|
}
|
|
14464
14480
|
/**
|
|
14465
14481
|
* The update path of both routes: the MERGED rule is judged, and only when the
|
|
@@ -14470,7 +14486,92 @@ function guardPatchSensorTransition(existing, patch, ctx) {
|
|
|
14470
14486
|
if (existing === null) return;
|
|
14471
14487
|
const merged = mergedFamilySubject(existing, patch);
|
|
14472
14488
|
if (merged === null) return;
|
|
14473
|
-
logRefusal(() => assertSensorTransitionDelivery(merged), ctx);
|
|
14489
|
+
logRefusal$1(() => assertSensorTransitionDelivery(merged), ctx);
|
|
14490
|
+
}
|
|
14491
|
+
//#endregion
|
|
14492
|
+
//#region src/notification-center/detection-audio-guard.ts
|
|
14493
|
+
var NcDetectionAudioRefusal = class extends Error {
|
|
14494
|
+
audioClasses;
|
|
14495
|
+
constructor(audioClasses) {
|
|
14496
|
+
super(`rule refused: a detection rule cannot name sounds (${audioClasses.join(", ")}) — create a Sound rule for them`);
|
|
14497
|
+
this.name = "NcDetectionAudioRefusal";
|
|
14498
|
+
this.audioClasses = audioClasses;
|
|
14499
|
+
}
|
|
14500
|
+
};
|
|
14501
|
+
/**
|
|
14502
|
+
* The audio classes a DETECTION rule carries (include and exclude), in stored
|
|
14503
|
+
* order. Empty for a Sound rule (its `audio` condition owns its sounds) and for
|
|
14504
|
+
* a system rule (it has no class scope).
|
|
14505
|
+
*/
|
|
14506
|
+
function audioClassesOnDetectionRule(rule) {
|
|
14507
|
+
if (require_clip_model_registry.isSystemDelivery(rule.delivery)) return [];
|
|
14508
|
+
if (rule.conditions.audio !== void 0) return [];
|
|
14509
|
+
return [...rule.conditions.classes ?? [], ...rule.conditions.classesExclude ?? []].filter(require_clip_model_registry.isAudioClassName);
|
|
14510
|
+
}
|
|
14511
|
+
function assertNoAudioClassOnDetectionRule(rule) {
|
|
14512
|
+
const audioClasses = audioClassesOnDetectionRule(rule);
|
|
14513
|
+
if (audioClasses.length > 0) throw new NcDetectionAudioRefusal(audioClasses);
|
|
14514
|
+
}
|
|
14515
|
+
/** Run the check; a refusal is dropped work, so it is logged before it is rethrown. */
|
|
14516
|
+
function logRefusal(check, ctx) {
|
|
14517
|
+
try {
|
|
14518
|
+
check();
|
|
14519
|
+
} catch (err) {
|
|
14520
|
+
if (err instanceof NcDetectionAudioRefusal) warnRefusal(ctx.logger, err, ctx);
|
|
14521
|
+
throw err;
|
|
14522
|
+
}
|
|
14523
|
+
}
|
|
14524
|
+
function warnRefusal(logger, err, ctx) {
|
|
14525
|
+
logger.warn("nc rule refused: a detection rule names sounds", { meta: {
|
|
14526
|
+
...ctx.ruleId !== void 0 ? { ruleId: ctx.ruleId } : {},
|
|
14527
|
+
by: ctx.by,
|
|
14528
|
+
audioClasses: err.audioClasses.join(", ")
|
|
14529
|
+
} });
|
|
14530
|
+
}
|
|
14531
|
+
/** The create path of both routes. */
|
|
14532
|
+
function guardRuleDetectionAudio(rule, ctx) {
|
|
14533
|
+
logRefusal(() => assertNoAudioClassOnDetectionRule(rule), ctx);
|
|
14534
|
+
}
|
|
14535
|
+
/**
|
|
14536
|
+
* The update path of both routes: the MERGED rule, only when the patch changes
|
|
14537
|
+
* it — and only the sounds the save ADDS. A legacy rule that already names a
|
|
14538
|
+
* sound stays editable (a threshold, a schedule, a zone); what is refused is
|
|
14539
|
+
* naming a new one.
|
|
14540
|
+
*/
|
|
14541
|
+
function guardPatchDetectionAudio(existing, patch, ctx) {
|
|
14542
|
+
if (existing === null) return;
|
|
14543
|
+
const merged = mergedFamilySubject(existing, patch);
|
|
14544
|
+
if (merged === null) return;
|
|
14545
|
+
const stored = new Set(audioClassesOnDetectionRule(existing).map(normalisedClass));
|
|
14546
|
+
const added = audioClassesOnDetectionRule(merged).filter((name) => !stored.has(normalisedClass(name)));
|
|
14547
|
+
if (added.length === 0) return;
|
|
14548
|
+
logRefusal(() => {
|
|
14549
|
+
throw new NcDetectionAudioRefusal(added);
|
|
14550
|
+
}, ctx);
|
|
14551
|
+
}
|
|
14552
|
+
function normalisedClass(name) {
|
|
14553
|
+
return name.trim().toLowerCase();
|
|
14554
|
+
}
|
|
14555
|
+
/**
|
|
14556
|
+
* The loader's half: every stored detection rule that names a sound is KEPT
|
|
14557
|
+
* (it loads and fires as it always did) and reported at WARN with its id —
|
|
14558
|
+
* once per rule per process, never on every reload tick. Returns the ids it
|
|
14559
|
+
* reported this time, for the caller's bookkeeping.
|
|
14560
|
+
*/
|
|
14561
|
+
function warnLegacyAudioClassRules(rules, alreadyWarned, logger) {
|
|
14562
|
+
const reported = [];
|
|
14563
|
+
for (const rule of rules) {
|
|
14564
|
+
if (alreadyWarned.has(rule.id)) continue;
|
|
14565
|
+
const audioClasses = audioClassesOnDetectionRule(rule);
|
|
14566
|
+
if (audioClasses.length === 0) continue;
|
|
14567
|
+
logger.warn("notification rule is a detection rule naming sounds — kept as it is; recreate it as a Sound rule", { meta: {
|
|
14568
|
+
ruleId: rule.id,
|
|
14569
|
+
name: rule.name,
|
|
14570
|
+
audioClasses: audioClasses.join(", ")
|
|
14571
|
+
} });
|
|
14572
|
+
reported.push(rule.id);
|
|
14573
|
+
}
|
|
14574
|
+
return reported;
|
|
14474
14575
|
}
|
|
14475
14576
|
//#endregion
|
|
14476
14577
|
//#region src/notification-center/occupancy-watcher.ts
|
|
@@ -17221,6 +17322,10 @@ function makeNcActionHandlers(deps) {
|
|
|
17221
17322
|
logger: deps.logger,
|
|
17222
17323
|
by: c.userId
|
|
17223
17324
|
});
|
|
17325
|
+
guardRuleDetectionAudio(parsed, {
|
|
17326
|
+
logger: deps.logger,
|
|
17327
|
+
by: c.userId
|
|
17328
|
+
});
|
|
17224
17329
|
guardRuleDevicesExclude(parsed, {
|
|
17225
17330
|
logger: deps.logger,
|
|
17226
17331
|
by: c.userId
|
|
@@ -17257,6 +17362,11 @@ function makeNcActionHandlers(deps) {
|
|
|
17257
17362
|
by: c.userId,
|
|
17258
17363
|
ruleId: input.ruleId
|
|
17259
17364
|
});
|
|
17365
|
+
guardPatchDetectionAudio(existing, patch, {
|
|
17366
|
+
logger: deps.logger,
|
|
17367
|
+
by: c.userId,
|
|
17368
|
+
ruleId: input.ruleId
|
|
17369
|
+
});
|
|
17260
17370
|
guardPatchDevicesExclude(existing, patch, {
|
|
17261
17371
|
logger: deps.logger,
|
|
17262
17372
|
by: c.userId,
|
|
@@ -25420,6 +25530,8 @@ var NcRuleStore = class {
|
|
|
25420
25530
|
identityIdsByName;
|
|
25421
25531
|
/** Has a {@link load} ever completed? See the note there. */
|
|
25422
25532
|
loaded = false;
|
|
25533
|
+
/** Legacy detection rules naming sounds already reported (D690) — once each. */
|
|
25534
|
+
audioClassWarned = /* @__PURE__ */ new Set();
|
|
25423
25535
|
constructor(deps) {
|
|
25424
25536
|
this.store = deps.store;
|
|
25425
25537
|
this.ledger = new DurableLedger({
|
|
@@ -25454,6 +25566,7 @@ var NcRuleStore = class {
|
|
|
25454
25566
|
async load() {
|
|
25455
25567
|
await this.ledger.load();
|
|
25456
25568
|
if (this.ledger.hasAnswered()) this.loaded = true;
|
|
25569
|
+
for (const id of warnLegacyAudioClassRules(this.ledger.snapshot(), this.audioClassWarned, this.logger)) this.audioClassWarned.add(id);
|
|
25457
25570
|
this.logger.debug("notification rules loaded", { meta: { rules: this.ledger.size } });
|
|
25458
25571
|
}
|
|
25459
25572
|
/**
|
|
@@ -33685,6 +33798,10 @@ var NotificationCenter = class NotificationCenter {
|
|
|
33685
33798
|
logger: this.logger,
|
|
33686
33799
|
by: caller.userId
|
|
33687
33800
|
});
|
|
33801
|
+
guardRuleDetectionAudio(rule, {
|
|
33802
|
+
logger: this.logger,
|
|
33803
|
+
by: caller.userId
|
|
33804
|
+
});
|
|
33688
33805
|
guardRuleDevicesExclude(rule, {
|
|
33689
33806
|
logger: this.logger,
|
|
33690
33807
|
by: caller.userId
|
|
@@ -33716,6 +33833,11 @@ var NotificationCenter = class NotificationCenter {
|
|
|
33716
33833
|
by: caller.userId,
|
|
33717
33834
|
ruleId
|
|
33718
33835
|
});
|
|
33836
|
+
guardPatchDetectionAudio(existing, patch, {
|
|
33837
|
+
logger: this.logger,
|
|
33838
|
+
by: caller.userId,
|
|
33839
|
+
ruleId
|
|
33840
|
+
});
|
|
33719
33841
|
guardPatchDevicesExclude(existing, patch, {
|
|
33720
33842
|
logger: this.logger,
|
|
33721
33843
|
by: caller.userId,
|
|
@@ -67320,6 +67442,22 @@ function messageOf(err) {
|
|
|
67320
67442
|
function isDetailNotAttachedRefusal(err) {
|
|
67321
67443
|
return messageOf(err).includes(DETAIL_NOT_ATTACHED_MARKER);
|
|
67322
67444
|
}
|
|
67445
|
+
/**
|
|
67446
|
+
* The runner's TYPED handle-expired refusal (D688): the frame handle a dispatch
|
|
67447
|
+
* named is past the native lease age bound, and the registry will refuse it on
|
|
67448
|
+
* every later ask. TWIN of `addon-pipeline/src/pipeline-runner/detail-park.ts`
|
|
67449
|
+
* `DETAIL_HANDLE_EXPIRED_MARKER` — keep the two identical.
|
|
67450
|
+
*
|
|
67451
|
+
* Why the dispatcher cares: unlike not-attached this is about ONE handle, not
|
|
67452
|
+
* the session — the parked frame still serves — but the handle itself is
|
|
67453
|
+
* finished. Naming it again (next tick, or through the native-strict crop rung)
|
|
67454
|
+
* can only be refused again; on the live fleet that was ~7 refusals per handle.
|
|
67455
|
+
*/
|
|
67456
|
+
var DETAIL_HANDLE_EXPIRED_MARKER = "runDetailSubtree-refused:handle-expired";
|
|
67457
|
+
/** Did this `runDetailSubtree` error carry the runner's handle-expired marker? */
|
|
67458
|
+
function isDetailHandleExpiredRefusal(err) {
|
|
67459
|
+
return messageOf(err).includes(DETAIL_HANDLE_EXPIRED_MARKER);
|
|
67460
|
+
}
|
|
67323
67461
|
//#endregion
|
|
67324
67462
|
//#region src/pipeline-analytics/detail-scheduler.ts
|
|
67325
67463
|
/** Default backoff/period when a step's cadence omits `minIntervalMs`. */
|
|
@@ -67776,6 +67914,7 @@ var TrackDetailDispatcher = class {
|
|
|
67776
67914
|
className: frame.className
|
|
67777
67915
|
};
|
|
67778
67916
|
const steps = composeDetailSteps(req, (trackId) => this.deps.hasTrackLabel?.(trackId) ?? false);
|
|
67917
|
+
let handleRefused = false;
|
|
67779
67918
|
if (frame.frameHandle !== void 0) try {
|
|
67780
67919
|
const primary = await this.deps.runDetailSubtree({
|
|
67781
67920
|
deviceId,
|
|
@@ -67789,7 +67928,10 @@ var TrackDetailDispatcher = class {
|
|
|
67789
67928
|
this.warnNotAttachedThrottled(deviceId, dev, req);
|
|
67790
67929
|
return null;
|
|
67791
67930
|
}
|
|
67792
|
-
|
|
67931
|
+
if (isDetailHandleExpiredRefusal(err)) {
|
|
67932
|
+
handleRefused = true;
|
|
67933
|
+
this.retireRefusedHandle(deviceId, dev, req, frame);
|
|
67934
|
+
} else this.deps.logger.debug("detail primary call failed — trying frame fallback", {
|
|
67793
67935
|
tags: { deviceId },
|
|
67794
67936
|
meta: {
|
|
67795
67937
|
trackId: req.trackId,
|
|
@@ -67839,7 +67981,7 @@ var TrackDetailDispatcher = class {
|
|
|
67839
67981
|
}
|
|
67840
67982
|
});
|
|
67841
67983
|
}
|
|
67842
|
-
if (this.deps.captureCropBase64 !== void 0) try {
|
|
67984
|
+
if (this.deps.captureCropBase64 !== void 0 && frame.frameHandle !== void 0 && !handleRefused) try {
|
|
67843
67985
|
const cropJpeg = await this.deps.captureCropBase64(frame);
|
|
67844
67986
|
if (cropJpeg !== null) {
|
|
67845
67987
|
const retry = await this.deps.runDetailSubtree({
|
|
@@ -67868,6 +68010,35 @@ var TrackDetailDispatcher = class {
|
|
|
67868
68010
|
return null;
|
|
67869
68011
|
}
|
|
67870
68012
|
/**
|
|
68013
|
+
* The runner refused `frame`'s handle as past the lease age bound (D688).
|
|
68014
|
+
* That verdict is final for the handle, so the track's stored association
|
|
68015
|
+
* loses it: later dispatches skip rung 1 and the native-strict crop rung
|
|
68016
|
+
* (both would name the same handle and be refused again — ~7 times per
|
|
68017
|
+
* handle on the live fleet) and go straight to the parked frame, until a
|
|
68018
|
+
* newly OBSERVED frame brings a fresh handle via {@link onFrame}.
|
|
68019
|
+
*
|
|
68020
|
+
* Only the refused handle is dropped: if a newer frame replaced it while the
|
|
68021
|
+
* dispatch was in flight, that one stands. The line is therefore emitted once
|
|
68022
|
+
* per (track, handle) — a concurrent dispatch refused on the same handle
|
|
68023
|
+
* finds it already gone and says nothing.
|
|
68024
|
+
*/
|
|
68025
|
+
retireRefusedHandle(deviceId, dev, req, frame) {
|
|
68026
|
+
const current = dev.tracks.get(req.trackId);
|
|
68027
|
+
if (current === void 0 || current.frameHandle !== frame.frameHandle) return;
|
|
68028
|
+
const { frameHandle: refused, ...withoutHandle } = current;
|
|
68029
|
+
dev.tracks.set(req.trackId, withoutHandle);
|
|
68030
|
+
this.deps.logger.info("detail dispatch: frame handle refused as expired — retired for this track, parked frame only until a fresh observation", {
|
|
68031
|
+
tags: { deviceId },
|
|
68032
|
+
meta: {
|
|
68033
|
+
trackId: req.trackId,
|
|
68034
|
+
stepId: req.stepId,
|
|
68035
|
+
reason: "handle-expired",
|
|
68036
|
+
handle: refused === void 0 ? null : `${refused.shmId}#${refused.slot}#${refused.seq}`,
|
|
68037
|
+
parkedFrameRung: this.deps.captureParkedFrame !== void 0
|
|
68038
|
+
}
|
|
68039
|
+
});
|
|
68040
|
+
}
|
|
68041
|
+
/**
|
|
67871
68042
|
* The typed refusal is EXPECTED for a track that outlives the runner's
|
|
67872
68043
|
* detail park grace (a closing track's last dispatches), so it logs its own
|
|
67873
68044
|
* line — the reason is knowable, unlike the every-rung-missed warn — and
|