@camstack/addon-post-analysis 1.2.613 → 1.2.615
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-D6tdLEFB.mjs → clip-model-registry-BtK1fPgi.mjs} +21 -52
- package/dist/{clip-model-registry-CM-m6e1l.js → clip-model-registry-ycbGgfo_.js} +21 -52
- 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-PyXxg2Wg.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CnHfiisq.mjs} +3 -3
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CSQBGkvs.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-RCMuxWxC.mjs → hostInit-BGsx8mDn.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +899 -386
- package/dist/pipeline-analytics/index.mjs +898 -386
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-mydSlNUz.mjs +0 -26
|
@@ -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-ycbGgfo_.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");
|
|
@@ -4078,6 +4078,224 @@ function ncAlarmPanelPort(panel) {
|
|
|
4078
4078
|
};
|
|
4079
4079
|
}
|
|
4080
4080
|
//#endregion
|
|
4081
|
+
//#region src/durable/durable-record-body.ts
|
|
4082
|
+
/**
|
|
4083
|
+
* Zod body for phase-3 durable ledger record kinds. The engine validates on
|
|
4084
|
+
* write; each owner's `fromRecord` / `toValue` remain authoritative on read.
|
|
4085
|
+
*/
|
|
4086
|
+
var DurableLedgerBodySchema = require_clip_model_registry.record(require_clip_model_registry.string(), require_clip_model_registry.unknown());
|
|
4087
|
+
//#endregion
|
|
4088
|
+
//#region src/notification-center/nc-durable-record-kinds.ts
|
|
4089
|
+
/**
|
|
4090
|
+
* Record kinds for NC live-state ledgers (records plan phase 3, steps 25–36).
|
|
4091
|
+
*
|
|
4092
|
+
* Hosted in the pipeline-analytics addon (`pipeline-analytics` owner stamp).
|
|
4093
|
+
* Declared by the hub; every node reads and writes them through `ctx.records`.
|
|
4094
|
+
* The legacy settings-store handles (`durableLegacyKind`, `legacyCollectionKind`)
|
|
4095
|
+
* serve only a centre built WITHOUT `records` — the specs.
|
|
4096
|
+
*/
|
|
4097
|
+
/** The settings-store tables these kinds replaced; read only WITHOUT `ctx.records` (the specs). */
|
|
4098
|
+
var NC_LIVENESS_LEGACY = "notification-center:liveness";
|
|
4099
|
+
var NC_DEVICE_ALERTS_LEGACY = "notification-center:device-alerts";
|
|
4100
|
+
var NC_SYSTEM_STATE_LEGACY = "notification-center:system-state";
|
|
4101
|
+
var NC_OCCUPANCY_LEGACY = "notification-center:occupancy";
|
|
4102
|
+
var NC_DEVICE_PRUNES_LEGACY = "notification-center:device-prunes";
|
|
4103
|
+
var NC_STATE_SUMMARY_STAMPS_LEGACY = "notification-center:state-summary-stamps";
|
|
4104
|
+
var NC_ALERT_EPISODES_LEGACY = "notification-center:alert-episodes";
|
|
4105
|
+
var NC_META_LEGACY = "notification-center:meta";
|
|
4106
|
+
var NC_SNOOZES_LEGACY = "notification-center:snoozes";
|
|
4107
|
+
var NC_SNOOZE_SUPPRESSED_LEGACY = "notification-center:snooze-suppressed";
|
|
4108
|
+
var NC_TRACK_GROUPS_LEGACY = "notification-center:track-groups";
|
|
4109
|
+
var NC_OUTBOX_LEGACY = "notification-center:outbox";
|
|
4110
|
+
var NC_ARTIFACTS_LEGACY = "notification-center:artifacts";
|
|
4111
|
+
var ledger = (writeTrigger, database = "settings", retention = { none: "mirror load-all at boot; rows drop only via owner prune or device-removal hook" }) => ({
|
|
4112
|
+
schema: DurableLedgerBodySchema,
|
|
4113
|
+
class: "ledger",
|
|
4114
|
+
database,
|
|
4115
|
+
writeTrigger,
|
|
4116
|
+
device: "optional",
|
|
4117
|
+
at: "none",
|
|
4118
|
+
retention
|
|
4119
|
+
});
|
|
4120
|
+
/**
|
|
4121
|
+
* The shape the four QUERIED kinds share with the mirrors. Each spells its own
|
|
4122
|
+
* file, trigger, indexes and retention as literals (the records guards read them).
|
|
4123
|
+
* `device` and `at` stay what the first (empty) declaration said — the engine
|
|
4124
|
+
* refuses to change either — so every read is served by a DATA-field index, and
|
|
4125
|
+
* the camera rides the record envelope (`deviceId`) for the D865 removal.
|
|
4126
|
+
*/
|
|
4127
|
+
var QUERIED = {
|
|
4128
|
+
schema: DurableLedgerBodySchema,
|
|
4129
|
+
device: "optional",
|
|
4130
|
+
at: "none"
|
|
4131
|
+
};
|
|
4132
|
+
var ncDurableRecordKinds = require_clip_model_registry.defineRecordKinds({
|
|
4133
|
+
liveness: ledger("a liveness state TRANSITION the centre accepted (seed or flip); a no-flip writes nothing"),
|
|
4134
|
+
"device-alerts": ledger("a sustained device alert episode the centre confirmed or cleared"),
|
|
4135
|
+
"system-state": ledger("a system-state subject flip the state summary accepted"),
|
|
4136
|
+
"alert-episodes": ledger("an alert episode the centre notified (critical or error); its resolution deletes the row"),
|
|
4137
|
+
occupancy: ledger("a confirmed occupancy level write-through after the watcher decided an edge"),
|
|
4138
|
+
"device-prunes": ledger("one row recording what a deleted-device prune removed"),
|
|
4139
|
+
"state-summary-stamps": ledger("digest delivery stamps per rule kind"),
|
|
4140
|
+
meta: ledger("outbox boot-reconcile watermark and other single-key meta rows", "settings", { none: "one fixed key (watermark), overwritten in place, never deleted" }),
|
|
4141
|
+
snoozes: ledger("operator opens or closes a snooze window (write-through)"),
|
|
4142
|
+
"snooze-suppressed": {
|
|
4143
|
+
...QUERIED,
|
|
4144
|
+
class: "ledger",
|
|
4145
|
+
database: "events",
|
|
4146
|
+
writeTrigger: "aggregate count per (window, target, rule, camera) for suppressed notifications",
|
|
4147
|
+
indexes: [{
|
|
4148
|
+
name: "snooze",
|
|
4149
|
+
fields: ["snoozeId"]
|
|
4150
|
+
}],
|
|
4151
|
+
retention: {
|
|
4152
|
+
owner: "notification-center",
|
|
4153
|
+
reason: "deleted with its snooze window, 6 h after that window closes"
|
|
4154
|
+
}
|
|
4155
|
+
},
|
|
4156
|
+
"track-groups": {
|
|
4157
|
+
...QUERIED,
|
|
4158
|
+
class: "ledger",
|
|
4159
|
+
database: "events",
|
|
4160
|
+
writeTrigger: "one row per grouped delivery window opened at admission time",
|
|
4161
|
+
indexes: [
|
|
4162
|
+
{
|
|
4163
|
+
name: "open",
|
|
4164
|
+
fields: ["sealed"]
|
|
4165
|
+
},
|
|
4166
|
+
{
|
|
4167
|
+
name: "opened",
|
|
4168
|
+
fields: ["openedAt"]
|
|
4169
|
+
},
|
|
4170
|
+
{
|
|
4171
|
+
name: "device_opened",
|
|
4172
|
+
fields: ["deviceId", "openedAt"]
|
|
4173
|
+
},
|
|
4174
|
+
{
|
|
4175
|
+
name: "rule_opened",
|
|
4176
|
+
fields: ["ruleId", "openedAt"]
|
|
4177
|
+
},
|
|
4178
|
+
{
|
|
4179
|
+
name: "rule_device_opened",
|
|
4180
|
+
fields: [
|
|
4181
|
+
"ruleId",
|
|
4182
|
+
"deviceId",
|
|
4183
|
+
"openedAt"
|
|
4184
|
+
]
|
|
4185
|
+
}
|
|
4186
|
+
],
|
|
4187
|
+
retention: {
|
|
4188
|
+
owner: "notification-center",
|
|
4189
|
+
reason: "ring: the newest NC_GROUP_MAX_ROWS_PER_DEVICE rows per camera, trimmed on write"
|
|
4190
|
+
}
|
|
4191
|
+
},
|
|
4192
|
+
outbox: {
|
|
4193
|
+
...QUERIED,
|
|
4194
|
+
class: "ledger",
|
|
4195
|
+
database: "events",
|
|
4196
|
+
writeTrigger: "one row per (rule, dedupRef, target) enqueued for delivery",
|
|
4197
|
+
indexes: [
|
|
4198
|
+
{
|
|
4199
|
+
name: "created",
|
|
4200
|
+
fields: ["createdAt"]
|
|
4201
|
+
},
|
|
4202
|
+
{
|
|
4203
|
+
name: "rule_created",
|
|
4204
|
+
fields: ["ruleId", "createdAt"]
|
|
4205
|
+
},
|
|
4206
|
+
{
|
|
4207
|
+
name: "status_created",
|
|
4208
|
+
fields: ["status", "createdAt"]
|
|
4209
|
+
},
|
|
4210
|
+
{
|
|
4211
|
+
name: "device_created",
|
|
4212
|
+
fields: ["deviceId", "createdAt"]
|
|
4213
|
+
},
|
|
4214
|
+
{
|
|
4215
|
+
name: "rule_status_created",
|
|
4216
|
+
fields: [
|
|
4217
|
+
"ruleId",
|
|
4218
|
+
"status",
|
|
4219
|
+
"createdAt"
|
|
4220
|
+
]
|
|
4221
|
+
},
|
|
4222
|
+
{
|
|
4223
|
+
name: "rule_device_created",
|
|
4224
|
+
fields: [
|
|
4225
|
+
"ruleId",
|
|
4226
|
+
"deviceId",
|
|
4227
|
+
"createdAt"
|
|
4228
|
+
]
|
|
4229
|
+
},
|
|
4230
|
+
{
|
|
4231
|
+
name: "status_device_created",
|
|
4232
|
+
fields: [
|
|
4233
|
+
"status",
|
|
4234
|
+
"deviceId",
|
|
4235
|
+
"createdAt"
|
|
4236
|
+
]
|
|
4237
|
+
},
|
|
4238
|
+
{
|
|
4239
|
+
name: "rule_status_device_created",
|
|
4240
|
+
fields: [
|
|
4241
|
+
"ruleId",
|
|
4242
|
+
"status",
|
|
4243
|
+
"deviceId",
|
|
4244
|
+
"createdAt"
|
|
4245
|
+
]
|
|
4246
|
+
}
|
|
4247
|
+
],
|
|
4248
|
+
retention: {
|
|
4249
|
+
owner: "notification-center",
|
|
4250
|
+
reason: "terminal (sent/dead) rows older than 7 days pruned at boot; a pending row retries to dead first"
|
|
4251
|
+
},
|
|
4252
|
+
maxDataBytes: require_clip_model_registry.RECORD_MAX_DATA_BYTES
|
|
4253
|
+
},
|
|
4254
|
+
artifacts: {
|
|
4255
|
+
...QUERIED,
|
|
4256
|
+
class: "ledger",
|
|
4257
|
+
database: "events",
|
|
4258
|
+
writeTrigger: "retained notification artefact metadata indexed for lookup and expiry",
|
|
4259
|
+
indexes: [{
|
|
4260
|
+
name: "captured",
|
|
4261
|
+
fields: ["capturedAt"]
|
|
4262
|
+
}, {
|
|
4263
|
+
name: "expires",
|
|
4264
|
+
fields: ["expiresAt"]
|
|
4265
|
+
}],
|
|
4266
|
+
retention: {
|
|
4267
|
+
owner: "notification-center",
|
|
4268
|
+
reason: "falls with its bytes: expired rows on every node, orphans against the shelf on the producer node"
|
|
4269
|
+
}
|
|
4270
|
+
}
|
|
4271
|
+
});
|
|
4272
|
+
/**
|
|
4273
|
+
* The settings-store table each kind stands in for when a centre is built
|
|
4274
|
+
* WITHOUT `ctx.records` - the specs' fake store (`legacyCollectionKind`,
|
|
4275
|
+
* `durableLegacyKind`). Production never reads it: every node has
|
|
4276
|
+
* `ctx.records`, and the operator dropped these tables offline (D900).
|
|
4277
|
+
*/
|
|
4278
|
+
var NC_DURABLE_LEGACY_TABLES = {
|
|
4279
|
+
liveness: NC_LIVENESS_LEGACY,
|
|
4280
|
+
"device-alerts": NC_DEVICE_ALERTS_LEGACY,
|
|
4281
|
+
"system-state": NC_SYSTEM_STATE_LEGACY,
|
|
4282
|
+
"alert-episodes": NC_ALERT_EPISODES_LEGACY,
|
|
4283
|
+
occupancy: NC_OCCUPANCY_LEGACY,
|
|
4284
|
+
"device-prunes": NC_DEVICE_PRUNES_LEGACY,
|
|
4285
|
+
"state-summary-stamps": NC_STATE_SUMMARY_STAMPS_LEGACY,
|
|
4286
|
+
meta: NC_META_LEGACY,
|
|
4287
|
+
snoozes: NC_SNOOZES_LEGACY,
|
|
4288
|
+
"snooze-suppressed": NC_SNOOZE_SUPPRESSED_LEGACY,
|
|
4289
|
+
"track-groups": NC_TRACK_GROUPS_LEGACY,
|
|
4290
|
+
outbox: NC_OUTBOX_LEGACY,
|
|
4291
|
+
artifacts: NC_ARTIFACTS_LEGACY
|
|
4292
|
+
};
|
|
4293
|
+
/** The legacy table of an NC durable kind by name, or `undefined` for any other kind. */
|
|
4294
|
+
function ncDurableLegacyTableOf(kind) {
|
|
4295
|
+
for (const [name, table] of Object.entries(NC_DURABLE_LEGACY_TABLES)) if (name === kind) return table;
|
|
4296
|
+
}
|
|
4297
|
+
Object.values(NC_DURABLE_LEGACY_TABLES);
|
|
4298
|
+
//#endregion
|
|
4081
4299
|
//#region src/durable/legacy-collection-kind.ts
|
|
4082
4300
|
/** Bounds of a half-open range on the legacy filter (the store has no `>=`). */
|
|
4083
4301
|
var RANGE_MIN = -864e13;
|
|
@@ -4223,8 +4441,8 @@ var LegacyCollectionKind = class {
|
|
|
4223
4441
|
/** The handle an NC owner reads and writes through: records when present, else the legacy table. */
|
|
4224
4442
|
function queriedKind(definition, deps) {
|
|
4225
4443
|
if (deps.records !== void 0) return deps.records.kind(definition);
|
|
4226
|
-
const collection = definition.
|
|
4227
|
-
if (collection === void 0) throw new Error(`kind "${definition.name}"
|
|
4444
|
+
const collection = ncDurableLegacyTableOf(definition.name);
|
|
4445
|
+
if (collection === void 0) throw new Error(`kind "${definition.name}" has no legacy table to stand in for it`);
|
|
4228
4446
|
return new LegacyCollectionKind({
|
|
4229
4447
|
definition,
|
|
4230
4448
|
collection,
|
|
@@ -4269,203 +4487,6 @@ async function collectRecordPages(kind, q, max) {
|
|
|
4269
4487
|
}
|
|
4270
4488
|
}
|
|
4271
4489
|
//#endregion
|
|
4272
|
-
//#region src/durable/durable-record-body.ts
|
|
4273
|
-
/**
|
|
4274
|
-
* Zod body for phase-3 durable ledger record kinds. The engine validates on
|
|
4275
|
-
* write; each owner's `fromRecord` / `toValue` remain authoritative on read.
|
|
4276
|
-
*/
|
|
4277
|
-
var DurableLedgerBodySchema = require_clip_model_registry.record(require_clip_model_registry.string(), require_clip_model_registry.unknown());
|
|
4278
|
-
//#endregion
|
|
4279
|
-
//#region src/notification-center/nc-durable-record-kinds.ts
|
|
4280
|
-
/**
|
|
4281
|
-
* Record kinds for NC live-state ledgers (records plan phase 3, steps 25–36).
|
|
4282
|
-
*
|
|
4283
|
-
* Hosted in the pipeline-analytics addon (`pipeline-analytics` owner stamp).
|
|
4284
|
-
* Declared by the hub; every node reads and writes them through `ctx.records`.
|
|
4285
|
-
* The legacy settings-store handles (`durableLegacyKind`, `legacyCollectionKind`)
|
|
4286
|
-
* serve only a centre built WITHOUT `records` — the specs.
|
|
4287
|
-
*/
|
|
4288
|
-
/** Legacy collection names — keep in sync with owner constants. */
|
|
4289
|
-
var NC_LIVENESS_LEGACY = "notification-center:liveness";
|
|
4290
|
-
var NC_DEVICE_ALERTS_LEGACY = "notification-center:device-alerts";
|
|
4291
|
-
var NC_SYSTEM_STATE_LEGACY = "notification-center:system-state";
|
|
4292
|
-
var NC_OCCUPANCY_LEGACY = "notification-center:occupancy";
|
|
4293
|
-
var NC_DEVICE_PRUNES_LEGACY = "notification-center:device-prunes";
|
|
4294
|
-
var NC_STATE_SUMMARY_STAMPS_LEGACY = "notification-center:state-summary-stamps";
|
|
4295
|
-
var NC_ALERT_EPISODES_LEGACY = "notification-center:alert-episodes";
|
|
4296
|
-
var NC_META_LEGACY = "notification-center:meta";
|
|
4297
|
-
var NC_SNOOZES_LEGACY = "notification-center:snoozes";
|
|
4298
|
-
var NC_SNOOZE_SUPPRESSED_LEGACY = "notification-center:snooze-suppressed";
|
|
4299
|
-
var NC_TRACK_GROUPS_LEGACY = "notification-center:track-groups";
|
|
4300
|
-
var NC_OUTBOX_LEGACY = "notification-center:outbox";
|
|
4301
|
-
var NC_ARTIFACTS_LEGACY = "notification-center:artifacts";
|
|
4302
|
-
var ledger = (adoptsFrom, writeTrigger, database = "settings", retention = { none: "mirror load-all at boot; rows drop only via owner prune or device-removal hook" }) => ({
|
|
4303
|
-
schema: DurableLedgerBodySchema,
|
|
4304
|
-
class: "ledger",
|
|
4305
|
-
database,
|
|
4306
|
-
writeTrigger,
|
|
4307
|
-
device: "optional",
|
|
4308
|
-
at: "none",
|
|
4309
|
-
retention,
|
|
4310
|
-
adoptsFrom
|
|
4311
|
-
});
|
|
4312
|
-
/**
|
|
4313
|
-
* The shape the four QUERIED kinds share with the mirrors. Each spells its own
|
|
4314
|
-
* file, trigger, indexes and retention as literals (the records guards read them).
|
|
4315
|
-
* `device` and `at` stay what the first (empty) declaration said — the engine
|
|
4316
|
-
* refuses to change either — so every read is served by a DATA-field index, and
|
|
4317
|
-
* the camera rides the record envelope (`deviceId`) for the D865 removal.
|
|
4318
|
-
*/
|
|
4319
|
-
var QUERIED = {
|
|
4320
|
-
schema: DurableLedgerBodySchema,
|
|
4321
|
-
device: "optional",
|
|
4322
|
-
at: "none"
|
|
4323
|
-
};
|
|
4324
|
-
var ncDurableRecordKinds = require_clip_model_registry.defineRecordKinds({
|
|
4325
|
-
liveness: ledger(NC_LIVENESS_LEGACY, "a liveness state TRANSITION the centre accepted (seed or flip); a no-flip writes nothing"),
|
|
4326
|
-
"device-alerts": ledger(NC_DEVICE_ALERTS_LEGACY, "a sustained device alert episode the centre confirmed or cleared"),
|
|
4327
|
-
"system-state": ledger(NC_SYSTEM_STATE_LEGACY, "a system-state subject flip the state summary accepted"),
|
|
4328
|
-
"alert-episodes": ledger(NC_ALERT_EPISODES_LEGACY, "an alert episode the centre notified (critical or error); its resolution deletes the row"),
|
|
4329
|
-
occupancy: ledger(NC_OCCUPANCY_LEGACY, "a confirmed occupancy level write-through after the watcher decided an edge"),
|
|
4330
|
-
"device-prunes": ledger(NC_DEVICE_PRUNES_LEGACY, "one row recording what a deleted-device prune removed"),
|
|
4331
|
-
"state-summary-stamps": ledger(NC_STATE_SUMMARY_STAMPS_LEGACY, "digest delivery stamps per rule kind"),
|
|
4332
|
-
meta: ledger(NC_META_LEGACY, "outbox boot-reconcile watermark and other single-key meta rows", "settings", { none: "one fixed key (watermark), overwritten in place, never deleted" }),
|
|
4333
|
-
snoozes: ledger(NC_SNOOZES_LEGACY, "operator opens or closes a snooze window (write-through)"),
|
|
4334
|
-
"snooze-suppressed": {
|
|
4335
|
-
...QUERIED,
|
|
4336
|
-
class: "ledger",
|
|
4337
|
-
database: "events",
|
|
4338
|
-
writeTrigger: "aggregate count per (window, target, rule, camera) for suppressed notifications",
|
|
4339
|
-
indexes: [{
|
|
4340
|
-
name: "snooze",
|
|
4341
|
-
fields: ["snoozeId"]
|
|
4342
|
-
}],
|
|
4343
|
-
retention: {
|
|
4344
|
-
owner: "notification-center",
|
|
4345
|
-
reason: "deleted with its snooze window, 6 h after that window closes"
|
|
4346
|
-
},
|
|
4347
|
-
adoptsFrom: NC_SNOOZE_SUPPRESSED_LEGACY
|
|
4348
|
-
},
|
|
4349
|
-
"track-groups": {
|
|
4350
|
-
...QUERIED,
|
|
4351
|
-
class: "ledger",
|
|
4352
|
-
database: "events",
|
|
4353
|
-
writeTrigger: "one row per grouped delivery window opened at admission time",
|
|
4354
|
-
indexes: [
|
|
4355
|
-
{
|
|
4356
|
-
name: "open",
|
|
4357
|
-
fields: ["sealed"]
|
|
4358
|
-
},
|
|
4359
|
-
{
|
|
4360
|
-
name: "opened",
|
|
4361
|
-
fields: ["openedAt"]
|
|
4362
|
-
},
|
|
4363
|
-
{
|
|
4364
|
-
name: "device_opened",
|
|
4365
|
-
fields: ["deviceId", "openedAt"]
|
|
4366
|
-
},
|
|
4367
|
-
{
|
|
4368
|
-
name: "rule_opened",
|
|
4369
|
-
fields: ["ruleId", "openedAt"]
|
|
4370
|
-
},
|
|
4371
|
-
{
|
|
4372
|
-
name: "rule_device_opened",
|
|
4373
|
-
fields: [
|
|
4374
|
-
"ruleId",
|
|
4375
|
-
"deviceId",
|
|
4376
|
-
"openedAt"
|
|
4377
|
-
]
|
|
4378
|
-
}
|
|
4379
|
-
],
|
|
4380
|
-
retention: {
|
|
4381
|
-
owner: "notification-center",
|
|
4382
|
-
reason: "ring: the newest NC_GROUP_MAX_ROWS_PER_DEVICE rows per camera, trimmed on write"
|
|
4383
|
-
},
|
|
4384
|
-
adoptsFrom: NC_TRACK_GROUPS_LEGACY
|
|
4385
|
-
},
|
|
4386
|
-
outbox: {
|
|
4387
|
-
...QUERIED,
|
|
4388
|
-
class: "ledger",
|
|
4389
|
-
database: "events",
|
|
4390
|
-
writeTrigger: "one row per (rule, dedupRef, target) enqueued for delivery",
|
|
4391
|
-
indexes: [
|
|
4392
|
-
{
|
|
4393
|
-
name: "created",
|
|
4394
|
-
fields: ["createdAt"]
|
|
4395
|
-
},
|
|
4396
|
-
{
|
|
4397
|
-
name: "rule_created",
|
|
4398
|
-
fields: ["ruleId", "createdAt"]
|
|
4399
|
-
},
|
|
4400
|
-
{
|
|
4401
|
-
name: "status_created",
|
|
4402
|
-
fields: ["status", "createdAt"]
|
|
4403
|
-
},
|
|
4404
|
-
{
|
|
4405
|
-
name: "device_created",
|
|
4406
|
-
fields: ["deviceId", "createdAt"]
|
|
4407
|
-
},
|
|
4408
|
-
{
|
|
4409
|
-
name: "rule_status_created",
|
|
4410
|
-
fields: [
|
|
4411
|
-
"ruleId",
|
|
4412
|
-
"status",
|
|
4413
|
-
"createdAt"
|
|
4414
|
-
]
|
|
4415
|
-
},
|
|
4416
|
-
{
|
|
4417
|
-
name: "rule_device_created",
|
|
4418
|
-
fields: [
|
|
4419
|
-
"ruleId",
|
|
4420
|
-
"deviceId",
|
|
4421
|
-
"createdAt"
|
|
4422
|
-
]
|
|
4423
|
-
},
|
|
4424
|
-
{
|
|
4425
|
-
name: "status_device_created",
|
|
4426
|
-
fields: [
|
|
4427
|
-
"status",
|
|
4428
|
-
"deviceId",
|
|
4429
|
-
"createdAt"
|
|
4430
|
-
]
|
|
4431
|
-
},
|
|
4432
|
-
{
|
|
4433
|
-
name: "rule_status_device_created",
|
|
4434
|
-
fields: [
|
|
4435
|
-
"ruleId",
|
|
4436
|
-
"status",
|
|
4437
|
-
"deviceId",
|
|
4438
|
-
"createdAt"
|
|
4439
|
-
]
|
|
4440
|
-
}
|
|
4441
|
-
],
|
|
4442
|
-
retention: {
|
|
4443
|
-
owner: "notification-center",
|
|
4444
|
-
reason: "terminal (sent/dead) rows older than 7 days pruned at boot; a pending row retries to dead first"
|
|
4445
|
-
},
|
|
4446
|
-
maxDataBytes: require_clip_model_registry.RECORD_MAX_DATA_BYTES,
|
|
4447
|
-
adoptsFrom: NC_OUTBOX_LEGACY
|
|
4448
|
-
},
|
|
4449
|
-
artifacts: {
|
|
4450
|
-
...QUERIED,
|
|
4451
|
-
class: "ledger",
|
|
4452
|
-
database: "events",
|
|
4453
|
-
writeTrigger: "retained notification artefact metadata indexed for lookup and expiry",
|
|
4454
|
-
indexes: [{
|
|
4455
|
-
name: "captured",
|
|
4456
|
-
fields: ["capturedAt"]
|
|
4457
|
-
}, {
|
|
4458
|
-
name: "expires",
|
|
4459
|
-
fields: ["expiresAt"]
|
|
4460
|
-
}],
|
|
4461
|
-
retention: {
|
|
4462
|
-
owner: "notification-center",
|
|
4463
|
-
reason: "falls with its bytes: expired rows on every node, orphans against the shelf on the producer node"
|
|
4464
|
-
},
|
|
4465
|
-
adoptsFrom: NC_ARTIFACTS_LEGACY
|
|
4466
|
-
}
|
|
4467
|
-
});
|
|
4468
|
-
//#endregion
|
|
4469
4490
|
//#region src/notification-center/artifact-index.ts
|
|
4470
4491
|
/**
|
|
4471
4492
|
* @durable class=ledger owner=notification-center database=events
|
|
@@ -9441,38 +9462,12 @@ var FaceRescoreJobStatusSchema = require_clip_model_registry.object({
|
|
|
9441
9462
|
* Record kinds owned by the pipeline-analytics addon (records plan phase 2,
|
|
9442
9463
|
* steps 7-11).
|
|
9443
9464
|
*
|
|
9444
|
-
* Each kind
|
|
9445
|
-
* name
|
|
9446
|
-
*
|
|
9447
|
-
*
|
|
9465
|
+
* Each kind replaced the legacy `pipeline-analytics:*` collection of the same
|
|
9466
|
+
* name; the records migration that copied them is closed (D900) and the tables
|
|
9467
|
+
* are gone (`retired-legacy-tables.ts`). The addon declares no
|
|
9468
|
+
* `execution.group`, so the hub owner stamp is the addon id
|
|
9448
9469
|
* (`pipeline-analytics`).
|
|
9449
9470
|
*/
|
|
9450
|
-
/** Legacy names for phase-3 analytics ledgers (keep in sync with owner constants). */
|
|
9451
|
-
var PA_PHANTOM_CELLS = "pipeline-analytics:phantom-cells";
|
|
9452
|
-
var PA_PACKAGE_BACKGROUND = "pipeline-analytics:package-background";
|
|
9453
|
-
var PA_STATIONARY_EVIDENCE = "pipeline-analytics:stationary-evidence";
|
|
9454
|
-
var PA_STATIONARY_OBJECTS = "pipeline-analytics:stationary-objects";
|
|
9455
|
-
var PA_SCENE_LATCH = "pipeline-analytics:scene-latch";
|
|
9456
|
-
var PA_MOTION_EVENTS = "pipeline-analytics:motion-events";
|
|
9457
|
-
var PA_AUDIO_EVENTS = "pipeline-analytics:audio-events";
|
|
9458
|
-
var PA_SENSOR_EVENTS = "pipeline-analytics:sensor-events";
|
|
9459
|
-
var PA_FACES = "pipeline-analytics:faces";
|
|
9460
|
-
var PA_PLATES = "pipeline-analytics:plates";
|
|
9461
|
-
var PA_TRACK_ALIASES = "pipeline-analytics:track-aliases";
|
|
9462
|
-
var PA_TRACK_PURITY = "pipeline-analytics:track-purity";
|
|
9463
|
-
var PA_TRACK_RELATIONS = "pipeline-analytics:track-relations";
|
|
9464
|
-
var PA_TRACKING_TAPES = "pipeline-analytics:tracking-tapes";
|
|
9465
|
-
var PA_BIRTH_DECISION_LEDGER = "pipeline-analytics:birth-decision-ledger";
|
|
9466
|
-
var PA_DEBUG_NOTE_ARCHIVE = "pipeline-analytics:debug-note-archive";
|
|
9467
|
-
var PA_OPS_LOG = "pipeline-analytics:ops-log";
|
|
9468
|
-
var PA_RETRAIN_FRAMES = "pipeline-analytics:retrain-frames";
|
|
9469
|
-
var PA_RETRAIN_ANNOTATIONS = "pipeline-analytics:retrain-annotations";
|
|
9470
|
-
var PA_RETRAIN_COLLECTIONS = "pipeline-analytics:retrain-collections";
|
|
9471
|
-
var PA_RETRAIN_COLLECTION_FRAMES = "pipeline-analytics:retrain-collection-frames";
|
|
9472
|
-
var PA_IDENTITIES = "pipeline-analytics:identities";
|
|
9473
|
-
var PA_IDENTITY_SAMPLES = "pipeline-analytics:identity-samples";
|
|
9474
|
-
var PA_VEHICLES = "pipeline-analytics:vehicles";
|
|
9475
|
-
var PA_VEHICLE_SAMPLES = "pipeline-analytics:vehicle-samples";
|
|
9476
9471
|
/**
|
|
9477
9472
|
* Kinds that must agree through ONE `transact` sit in one file under one family
|
|
9478
9473
|
* name, and the engine refuses a family split across files (records plan risk 8).
|
|
@@ -9499,7 +9494,9 @@ var MigrationMarkerBodySchema = require_clip_model_registry.object({
|
|
|
9499
9494
|
});
|
|
9500
9495
|
/** The orphan audit's resume position: one row, the whole map in one value on purpose. */
|
|
9501
9496
|
var SweepCursorBodySchema = require_clip_model_registry.object({
|
|
9502
|
-
|
|
9497
|
+
/** A number is an offset (a settings-store scope); a string is the records
|
|
9498
|
+
* engine's opaque keyset cursor (a scope walked through its kind). */
|
|
9499
|
+
scopes: require_clip_model_registry.record(require_clip_model_registry.string(), require_clip_model_registry.union([require_clip_model_registry.number(), require_clip_model_registry.string()])),
|
|
9503
9500
|
/** Absent on a row written before the walk existed — absent means "from the top". */
|
|
9504
9501
|
faceVectorCursor: require_clip_model_registry.number().optional(),
|
|
9505
9502
|
/** One cursor per CLIP index, keyed by index name. Absent on an older row. */
|
|
@@ -9524,8 +9521,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9524
9521
|
fields: ["deviceId"]
|
|
9525
9522
|
}],
|
|
9526
9523
|
retention: { none: "operator intent: a scene goes only through deleteScene, never by age" },
|
|
9527
|
-
maxDataBytes: require_clip_model_registry.RECORD_MAX_DATA_BYTES
|
|
9528
|
-
adoptsFrom: "pipeline-analytics:scenes"
|
|
9524
|
+
maxDataBytes: require_clip_model_registry.RECORD_MAX_DATA_BYTES
|
|
9529
9525
|
},
|
|
9530
9526
|
migrations: {
|
|
9531
9527
|
schema: MigrationMarkerBodySchema,
|
|
@@ -9534,8 +9530,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9534
9530
|
writeTrigger: "one marker row when a boot migration (label-tier-4g, audio-track-class-d189) completes without truncating",
|
|
9535
9531
|
device: "none",
|
|
9536
9532
|
at: "none",
|
|
9537
|
-
retention: { none: "its PRESENCE stops the migration re-scanning every track, face and event row on the next boot; bounded by the number of migrations ever written (two today)" }
|
|
9538
|
-
adoptsFrom: "pipeline-analytics:migrations"
|
|
9533
|
+
retention: { none: "its PRESENCE stops the migration re-scanning every track, face and event row on the next boot; bounded by the number of migrations ever written (two today)" }
|
|
9539
9534
|
},
|
|
9540
9535
|
"sweep-cursors": {
|
|
9541
9536
|
schema: SweepCursorBodySchema,
|
|
@@ -9544,8 +9539,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9544
9539
|
writeTrigger: "the end of every orphan-audit pass rewrites the one row holding every scope cursor",
|
|
9545
9540
|
device: "none",
|
|
9546
9541
|
at: "none",
|
|
9547
|
-
retention: { none: "one row (id orphan-audit); losing it costs a re-walk from offset 0, never a wrong delete" }
|
|
9548
|
-
adoptsFrom: "pipeline-analytics:sweep-cursors"
|
|
9542
|
+
retention: { none: "one row (id orphan-audit); losing it costs a re-walk from offset 0, never a wrong delete" }
|
|
9549
9543
|
},
|
|
9550
9544
|
"face-rescore-state": {
|
|
9551
9545
|
schema: FaceRescoreStateBodySchema,
|
|
@@ -9553,8 +9547,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9553
9547
|
writeTrigger: "a whole-archive face rescore (manual or scheduled) completes",
|
|
9554
9548
|
device: "none",
|
|
9555
9549
|
at: "none",
|
|
9556
|
-
retention: { none: "one row (id last-run); losing it costs one idempotent rescore on the next scheduler tick" }
|
|
9557
|
-
adoptsFrom: "pipeline-analytics:face-rescore-state"
|
|
9550
|
+
retention: { none: "one row (id last-run); losing it costs one idempotent rescore on the next scheduler tick" }
|
|
9558
9551
|
},
|
|
9559
9552
|
"phantom-cells": {
|
|
9560
9553
|
schema: DurableLedgerBodySchema,
|
|
@@ -9562,8 +9555,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9562
9555
|
writeTrigger: "a phantom cell closes and the short-motionless bar records it",
|
|
9563
9556
|
device: "required",
|
|
9564
9557
|
at: "none",
|
|
9565
|
-
retention: { none: "bounded by per-device cap in application code" }
|
|
9566
|
-
adoptsFrom: PA_PHANTOM_CELLS
|
|
9558
|
+
retention: { none: "bounded by per-device cap in application code" }
|
|
9567
9559
|
},
|
|
9568
9560
|
"package-background": {
|
|
9569
9561
|
schema: DurableLedgerBodySchema,
|
|
@@ -9571,8 +9563,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9571
9563
|
writeTrigger: "package background state accepted for a device",
|
|
9572
9564
|
device: "required",
|
|
9573
9565
|
at: "none",
|
|
9574
|
-
retention: { none: "one row per device key the owner tracks" }
|
|
9575
|
-
adoptsFrom: PA_PACKAGE_BACKGROUND
|
|
9566
|
+
retention: { none: "one row per device key the owner tracks" }
|
|
9576
9567
|
},
|
|
9577
9568
|
"stationary-evidence": {
|
|
9578
9569
|
schema: DurableLedgerBodySchema,
|
|
@@ -9580,8 +9571,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9580
9571
|
writeTrigger: "stationary evidence row written on the registry sweep",
|
|
9581
9572
|
device: "required",
|
|
9582
9573
|
at: "none",
|
|
9583
|
-
retention: { none: "owner sweep drops idle evidence; maxRowsPerDevice in kind metadata" }
|
|
9584
|
-
adoptsFrom: PA_STATIONARY_EVIDENCE
|
|
9574
|
+
retention: { none: "owner sweep drops idle evidence; maxRowsPerDevice in kind metadata" }
|
|
9585
9575
|
},
|
|
9586
9576
|
"stationary-objects": {
|
|
9587
9577
|
schema: DurableLedgerBodySchema,
|
|
@@ -9589,8 +9579,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9589
9579
|
writeTrigger: "parked object registry upsert or flush on the sweep interval",
|
|
9590
9580
|
device: "required",
|
|
9591
9581
|
at: "none",
|
|
9592
|
-
retention: { none: "rows drop when the object moves or TTL expires" }
|
|
9593
|
-
adoptsFrom: PA_STATIONARY_OBJECTS
|
|
9582
|
+
retention: { none: "rows drop when the object moves or TTL expires" }
|
|
9594
9583
|
},
|
|
9595
9584
|
"scene-latch": {
|
|
9596
9585
|
schema: DurableLedgerBodySchema,
|
|
@@ -9598,8 +9587,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9598
9587
|
writeTrigger: "scene monitor latch state write-through after a verdict",
|
|
9599
9588
|
device: "required",
|
|
9600
9589
|
at: "none",
|
|
9601
|
-
retention: { none: "one row per (deviceId, monitorId) scene latch" }
|
|
9602
|
-
adoptsFrom: PA_SCENE_LATCH
|
|
9590
|
+
retention: { none: "one row per (deviceId, monitorId) scene latch" }
|
|
9603
9591
|
},
|
|
9604
9592
|
"motion-events": {
|
|
9605
9593
|
schema: MotionEventBodySchema,
|
|
@@ -9616,8 +9604,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9616
9604
|
retention: {
|
|
9617
9605
|
owner: "device-window",
|
|
9618
9606
|
reason: "terminal age-keyed root swept with the device retention window (D61/D62)"
|
|
9619
|
-
}
|
|
9620
|
-
adoptsFrom: PA_MOTION_EVENTS
|
|
9607
|
+
}
|
|
9621
9608
|
},
|
|
9622
9609
|
"audio-events": {
|
|
9623
9610
|
schema: AudioEventBodySchema,
|
|
@@ -9634,8 +9621,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9634
9621
|
retention: {
|
|
9635
9622
|
owner: "device-window",
|
|
9636
9623
|
reason: "terminal age-keyed root on the device window, same as motion-events"
|
|
9637
|
-
}
|
|
9638
|
-
adoptsFrom: PA_AUDIO_EVENTS
|
|
9624
|
+
}
|
|
9639
9625
|
},
|
|
9640
9626
|
"sensor-events": {
|
|
9641
9627
|
schema: SensorEventBodySchema,
|
|
@@ -9655,8 +9641,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9655
9641
|
retention: {
|
|
9656
9642
|
owner: "device-window",
|
|
9657
9643
|
reason: "terminal age-keyed root on the camera retention window (D61)"
|
|
9658
|
-
}
|
|
9659
|
-
adoptsFrom: PA_SENSOR_EVENTS
|
|
9644
|
+
}
|
|
9660
9645
|
},
|
|
9661
9646
|
faces: {
|
|
9662
9647
|
schema: FaceLedgerBodySchema,
|
|
@@ -9683,8 +9668,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9683
9668
|
fields: ["trackId"]
|
|
9684
9669
|
}
|
|
9685
9670
|
],
|
|
9686
|
-
retention: { none: "track-owned (D61): rows leave with their track; enrolled faces exempt from capacity sweeps" }
|
|
9687
|
-
adoptsFrom: PA_FACES
|
|
9671
|
+
retention: { none: "track-owned (D61): rows leave with their track; enrolled faces exempt from capacity sweeps" }
|
|
9688
9672
|
},
|
|
9689
9673
|
identities: {
|
|
9690
9674
|
schema: IdentityConfigBodySchema,
|
|
@@ -9698,8 +9682,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9698
9682
|
name: "name",
|
|
9699
9683
|
fields: ["name"]
|
|
9700
9684
|
}],
|
|
9701
|
-
retention: { none: "operator delete only — losing the gallery un-enrols every person" }
|
|
9702
|
-
adoptsFrom: PA_IDENTITIES
|
|
9685
|
+
retention: { none: "operator delete only — losing the gallery un-enrols every person" }
|
|
9703
9686
|
},
|
|
9704
9687
|
"identity-samples": {
|
|
9705
9688
|
schema: IdentitySampleLedgerBodySchema,
|
|
@@ -9713,7 +9696,6 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9713
9696
|
fields: ["identityId", "addedAt"]
|
|
9714
9697
|
}],
|
|
9715
9698
|
retention: { none: "enrollment exempt — rows leave only on removeSample or deleteIdentity" },
|
|
9716
|
-
adoptsFrom: PA_IDENTITY_SAMPLES,
|
|
9717
9699
|
transactFamily: IDENTITY_GALLERY
|
|
9718
9700
|
},
|
|
9719
9701
|
vehicles: {
|
|
@@ -9728,8 +9710,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9728
9710
|
name: "name",
|
|
9729
9711
|
fields: ["name"]
|
|
9730
9712
|
}],
|
|
9731
|
-
retention: { none: "operator delete only — losing the gallery un-enrols every vehicle" }
|
|
9732
|
-
adoptsFrom: PA_VEHICLES
|
|
9713
|
+
retention: { none: "operator delete only — losing the gallery un-enrols every vehicle" }
|
|
9733
9714
|
},
|
|
9734
9715
|
"vehicle-samples": {
|
|
9735
9716
|
schema: VehicleSampleLedgerBodySchema,
|
|
@@ -9743,7 +9724,6 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9743
9724
|
fields: ["vehicleId", "addedAt"]
|
|
9744
9725
|
}],
|
|
9745
9726
|
retention: { none: "enrollment exempt — rows leave only on removeSample or deleteVehicle" },
|
|
9746
|
-
adoptsFrom: PA_VEHICLE_SAMPLES,
|
|
9747
9727
|
transactFamily: VEHICLE_GALLERY
|
|
9748
9728
|
},
|
|
9749
9729
|
plates: {
|
|
@@ -9767,8 +9747,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9767
9747
|
fields: ["recognizedVehicleId", "timestamp"]
|
|
9768
9748
|
}
|
|
9769
9749
|
],
|
|
9770
|
-
retention: { none: "track-owned (D61): rows leave with their track; corrected and enrolled reads exempt" }
|
|
9771
|
-
adoptsFrom: PA_PLATES
|
|
9750
|
+
retention: { none: "track-owned (D61): rows leave with their track; corrected and enrolled reads exempt" }
|
|
9772
9751
|
},
|
|
9773
9752
|
"track-aliases": {
|
|
9774
9753
|
schema: PaEventsLedgerBodySchema,
|
|
@@ -9784,8 +9763,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9784
9763
|
name: "device",
|
|
9785
9764
|
fields: ["deviceId"]
|
|
9786
9765
|
}],
|
|
9787
|
-
retention: { none: "track-owned via survivor `trackId`; widens retention cascade (D683)" }
|
|
9788
|
-
adoptsFrom: PA_TRACK_ALIASES
|
|
9766
|
+
retention: { none: "track-owned via survivor `trackId`; widens retention cascade (D683)" }
|
|
9789
9767
|
},
|
|
9790
9768
|
"track-purity": {
|
|
9791
9769
|
schema: PaEventsLedgerBodySchema,
|
|
@@ -9801,8 +9779,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9801
9779
|
name: "updated",
|
|
9802
9780
|
fields: ["updatedAt"]
|
|
9803
9781
|
}],
|
|
9804
|
-
retention: { none: "row-count ceiling MAX_TRACK_PURITIES in application code (D681)" }
|
|
9805
|
-
adoptsFrom: PA_TRACK_PURITY
|
|
9782
|
+
retention: { none: "row-count ceiling MAX_TRACK_PURITIES in application code (D681)" }
|
|
9806
9783
|
},
|
|
9807
9784
|
"track-relations": {
|
|
9808
9785
|
schema: PaEventsLedgerBodySchema,
|
|
@@ -9818,8 +9795,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9818
9795
|
name: "updated",
|
|
9819
9796
|
fields: ["updatedAt"]
|
|
9820
9797
|
}],
|
|
9821
|
-
retention: { none: "row-count ceiling MAX_TRACK_RELATIONS in application code (D681)" }
|
|
9822
|
-
adoptsFrom: PA_TRACK_RELATIONS
|
|
9798
|
+
retention: { none: "row-count ceiling MAX_TRACK_RELATIONS in application code (D681)" }
|
|
9823
9799
|
},
|
|
9824
9800
|
"tracking-tapes": {
|
|
9825
9801
|
schema: PaEventsLedgerBodySchema,
|
|
@@ -9832,8 +9808,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9832
9808
|
name: "device_created",
|
|
9833
9809
|
fields: ["deviceId", "createdAt"]
|
|
9834
9810
|
}],
|
|
9835
|
-
retention: { none: "promotion budget MAX_PROMOTED_TAPES — refused, never evicted by age" }
|
|
9836
|
-
adoptsFrom: PA_TRACKING_TAPES
|
|
9811
|
+
retention: { none: "promotion budget MAX_PROMOTED_TAPES — refused, never evicted by age" }
|
|
9837
9812
|
},
|
|
9838
9813
|
"birth-decision-ledger": {
|
|
9839
9814
|
schema: PaEventsLedgerBodySchema,
|
|
@@ -9849,8 +9824,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9849
9824
|
name: "device_at",
|
|
9850
9825
|
fields: ["deviceId", "at"]
|
|
9851
9826
|
}],
|
|
9852
|
-
retention: { none: "row-count ceiling MAX_BIRTH_DECISION_RECORDS in application code" }
|
|
9853
|
-
adoptsFrom: PA_BIRTH_DECISION_LEDGER
|
|
9827
|
+
retention: { none: "row-count ceiling MAX_BIRTH_DECISION_RECORDS in application code" }
|
|
9854
9828
|
},
|
|
9855
9829
|
"debug-note-archive": {
|
|
9856
9830
|
schema: PaEventsLedgerBodySchema,
|
|
@@ -9866,8 +9840,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9866
9840
|
name: "device_archived",
|
|
9867
9841
|
fields: ["deviceId", "archivedAt"]
|
|
9868
9842
|
}],
|
|
9869
|
-
retention: { none: "row-count ceiling MAX_ARCHIVED_DEBUG_NOTES in application code" }
|
|
9870
|
-
adoptsFrom: PA_DEBUG_NOTE_ARCHIVE
|
|
9843
|
+
retention: { none: "row-count ceiling MAX_ARCHIVED_DEBUG_NOTES in application code" }
|
|
9871
9844
|
},
|
|
9872
9845
|
"ops-log": {
|
|
9873
9846
|
schema: PaEventsLedgerBodySchema,
|
|
@@ -9886,8 +9859,7 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9886
9859
|
retention: {
|
|
9887
9860
|
ttlMs: 720 * 60 * 60 * 1e3,
|
|
9888
9861
|
from: "at"
|
|
9889
|
-
}
|
|
9890
|
-
adoptsFrom: PA_OPS_LOG
|
|
9862
|
+
}
|
|
9891
9863
|
},
|
|
9892
9864
|
"retrain-frames": {
|
|
9893
9865
|
schema: PaEventsLedgerBodySchema,
|
|
@@ -9904,7 +9876,6 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9904
9876
|
fields: ["deviceId", "complete"]
|
|
9905
9877
|
}],
|
|
9906
9878
|
retention: { none: "operator curation only; blobs on artifacts tenant (D781)" },
|
|
9907
|
-
adoptsFrom: PA_RETRAIN_FRAMES,
|
|
9908
9879
|
transactFamily: RETRAIN
|
|
9909
9880
|
},
|
|
9910
9881
|
"retrain-annotations": {
|
|
@@ -9929,7 +9900,6 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9929
9900
|
}
|
|
9930
9901
|
],
|
|
9931
9902
|
retention: { none: "leaves with frame deselect / operator delete" },
|
|
9932
|
-
adoptsFrom: PA_RETRAIN_ANNOTATIONS,
|
|
9933
9903
|
transactFamily: RETRAIN
|
|
9934
9904
|
},
|
|
9935
9905
|
"retrain-collections": {
|
|
@@ -9948,7 +9918,6 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9948
9918
|
fields: ["createdAt"]
|
|
9949
9919
|
}],
|
|
9950
9920
|
retention: { none: "deleted with operator deleteCollection" },
|
|
9951
|
-
adoptsFrom: PA_RETRAIN_COLLECTIONS,
|
|
9952
9921
|
transactFamily: RETRAIN
|
|
9953
9922
|
},
|
|
9954
9923
|
"retrain-collection-frames": {
|
|
@@ -9966,14 +9935,13 @@ var pipelineAnalyticsRecordKinds = require_clip_model_registry.defineRecordKinds
|
|
|
9966
9935
|
fields: ["frameId"]
|
|
9967
9936
|
}],
|
|
9968
9937
|
retention: { none: "membership only; frame pixels live in retrain-frames" },
|
|
9969
|
-
adoptsFrom: PA_RETRAIN_COLLECTION_FRAMES,
|
|
9970
9938
|
transactFamily: RETRAIN
|
|
9971
9939
|
}
|
|
9972
9940
|
});
|
|
9973
9941
|
//#endregion
|
|
9974
9942
|
//#region src/pipeline-analytics/scene/scene-config-ledger.ts
|
|
9975
9943
|
/** The engine's page cap for one query; a reseed pages by it, bounded by the ledger limit. */
|
|
9976
|
-
var PAGE_SIZE$
|
|
9944
|
+
var PAGE_SIZE$2 = 1e3;
|
|
9977
9945
|
/**
|
|
9978
9946
|
* A row whose monitor JSON no longer validates is SKIPPED, not repaired: a
|
|
9979
9947
|
* half-parsed scene would report a verdict about a region it cannot describe.
|
|
@@ -10011,7 +9979,7 @@ var SceneConfigLedger = class {
|
|
|
10011
9979
|
let seen = 0;
|
|
10012
9980
|
let cursor;
|
|
10013
9981
|
for (;;) {
|
|
10014
|
-
const limit = Math.min(PAGE_SIZE$
|
|
9982
|
+
const limit = Math.min(PAGE_SIZE$2, this.loadLimit - seen);
|
|
10015
9983
|
if (limit <= 0) break;
|
|
10016
9984
|
const page = await this.kind.query({
|
|
10017
9985
|
limit,
|
|
@@ -12058,6 +12026,28 @@ var EventLedgerCollection = class EventLedgerCollection {
|
|
|
12058
12026
|
if (mapped.limit !== void 0 && out.length > mapped.limit) return out.slice(0, mapped.limit);
|
|
12059
12027
|
return out;
|
|
12060
12028
|
}
|
|
12029
|
+
/**
|
|
12030
|
+
* One page of the whole kind in `id` order, resumed from the records
|
|
12031
|
+
* engine's OPAQUE keyset cursor (`null` = from the top). The answer's
|
|
12032
|
+
* `nextCursor` is `null` at the end. A keyset cursor names the last row
|
|
12033
|
+
* read, so deleting rows behind it never shifts what comes next — the
|
|
12034
|
+
* reason a bulk walk that reclaims as it goes needs this, not an offset.
|
|
12035
|
+
*
|
|
12036
|
+
* Records only: without `ctx.records` the caller pages the settings store
|
|
12037
|
+
* itself, and asking here throws rather than inventing a cursor.
|
|
12038
|
+
*/
|
|
12039
|
+
async pageById(cursor, limit) {
|
|
12040
|
+
if (this.kind === null) throw new Error(`event ledger ${this.collection}: pageById needs the records kind`);
|
|
12041
|
+
const page = await this.kind.query({
|
|
12042
|
+
order: "id",
|
|
12043
|
+
limit,
|
|
12044
|
+
...cursor === null ? {} : { cursor }
|
|
12045
|
+
});
|
|
12046
|
+
return {
|
|
12047
|
+
rows: page.records.map((row) => recordsRowToLegacy(row, this.legacyAtField)),
|
|
12048
|
+
nextCursor: page.nextCursor
|
|
12049
|
+
};
|
|
12050
|
+
}
|
|
12061
12051
|
async getManyByIds(ids) {
|
|
12062
12052
|
if (ids.length === 0) return [];
|
|
12063
12053
|
if (this.kind === null) return (await this.store.query.query({
|
|
@@ -13184,11 +13174,35 @@ var FaceStore = class {
|
|
|
13184
13174
|
* carries no `embeddingModelId`, and those rows were all `arcface-r100`, so
|
|
13185
13175
|
* that is what they are stamped with — a KNOWN historical fact, not a
|
|
13186
13176
|
* fallback guess. A row that names some other model keeps its own.
|
|
13177
|
+
*
|
|
13178
|
+
* On a records hub the rows are paged through the faces LEDGER: the legacy
|
|
13179
|
+
* table is dropped there, and the adoption copied each row's body whole —
|
|
13180
|
+
* an `embedding` the pass had not drained yet included — so a records-era
|
|
13181
|
+
* row can still need converting (the same shape as `IdentityStore`).
|
|
13187
13182
|
*/
|
|
13188
13183
|
conversionSpec() {
|
|
13184
|
+
const ledger = this.ledger;
|
|
13185
|
+
const recordsPaging = ledger.usesRecords ? {
|
|
13186
|
+
pageRows: (limit, offset) => ledger.query({
|
|
13187
|
+
orderBy: {
|
|
13188
|
+
field: "id",
|
|
13189
|
+
direction: "asc"
|
|
13190
|
+
},
|
|
13191
|
+
limit,
|
|
13192
|
+
offset
|
|
13193
|
+
}, [
|
|
13194
|
+
"deviceId",
|
|
13195
|
+
"timestamp",
|
|
13196
|
+
"trackId",
|
|
13197
|
+
"embeddingModelId",
|
|
13198
|
+
"embedding"
|
|
13199
|
+
]),
|
|
13200
|
+
drainEmbedding: (id) => ledger.update(id, { embedding: [] })
|
|
13201
|
+
} : {};
|
|
13189
13202
|
return {
|
|
13190
13203
|
collection: FACES_COLLECTION,
|
|
13191
13204
|
vectors: this.vectors,
|
|
13205
|
+
...recordsPaging,
|
|
13192
13206
|
write: (id, embedding, data) => {
|
|
13193
13207
|
const deviceId = data["deviceId"];
|
|
13194
13208
|
const timestamp = data["timestamp"];
|
|
@@ -38437,22 +38451,8 @@ async function migrateLegacyRuleActions(actions, createSequence) {
|
|
|
38437
38451
|
* The notification-center module is hosted inside the pipeline-analytics addon.
|
|
38438
38452
|
* That addon declares no `execution.group`, so the UDS owner stamp is the addon
|
|
38439
38453
|
* id: `pipeline-analytics`. Declared hub-only (agents have no `ctx.records`).
|
|
38440
|
-
* One deploy covers all 7 kinds
|
|
38441
|
-
*/
|
|
38442
|
-
/** Legacy collection for the text-catalog document. */
|
|
38443
|
-
var NC_TEXTS_COLLECTION$1 = "notification-center:texts";
|
|
38444
|
-
/** Legacy collection for shared action sequences. */
|
|
38445
|
-
var NC_SEQUENCES_COLLECTION$1 = "notification-center:sequences";
|
|
38446
|
-
/** Legacy collection for per-rule AI sections. */
|
|
38447
|
-
var NC_RULE_AI_COLLECTION$1 = "notification-center:rule-ai";
|
|
38448
|
-
/** Legacy collection for calendar-events rules. */
|
|
38449
|
-
var NC_CALENDAR_EVENTS_RULES_COLLECTION$1 = "notification-center:calendar-events-rules";
|
|
38450
|
-
/** Legacy collection for multi-camera digest rules. */
|
|
38451
|
-
var NC_SUMMARY_RULES_COLLECTION$1 = "notification-center:summary-rules";
|
|
38452
|
-
/** Legacy collection for timelapse rules. */
|
|
38453
|
-
var NC_TIMELAPSE_RULES_COLLECTION$1 = "notification-center:timelapse-rules";
|
|
38454
|
-
/** Legacy collection for notification rules. */
|
|
38455
|
-
var NC_RULES_COLLECTION$1 = "notification-center:rules";
|
|
38454
|
+
* One deploy covers all 7 kinds.
|
|
38455
|
+
*/
|
|
38456
38456
|
/**
|
|
38457
38457
|
* The hub language and operator-authored per-key overrides.
|
|
38458
38458
|
* One row, id `texts`. Losing it degrades to the shipped English catalog.
|
|
@@ -38494,8 +38494,7 @@ var ncRecordKinds = require_clip_model_registry.defineRecordKinds({
|
|
|
38494
38494
|
writeTrigger: "an operator changes the hub language (setLanguage) or replaces the per-key override set (setOverrides)",
|
|
38495
38495
|
device: "none",
|
|
38496
38496
|
at: "none",
|
|
38497
|
-
retention: { none: "one row (id `texts`); losing it costs the chosen language and household wording, never a notification — every read degrades to the shipped English catalog" }
|
|
38498
|
-
adoptsFrom: NC_TEXTS_COLLECTION$1
|
|
38497
|
+
retention: { none: "one row (id `texts`); losing it costs the chosen language and household wording, never a notification — every read degrades to the shipped English catalog" }
|
|
38499
38498
|
},
|
|
38500
38499
|
/**
|
|
38501
38500
|
* Shared action sequences — named, ordered runs of steps with their own throttle.
|
|
@@ -38507,8 +38506,7 @@ var ncRecordKinds = require_clip_model_registry.defineRecordKinds({
|
|
|
38507
38506
|
writeTrigger: "an admin creates/edits/deletes a shared sequence via notificationRules.createSequence / updateSequence / deleteSequence, or the one-shot boot migration that materialises legacy inline sequences",
|
|
38508
38507
|
device: "none",
|
|
38509
38508
|
at: "none",
|
|
38510
|
-
retention: { none: "a row goes only when an admin deletes it; bounded by the sequence set a human is willing to author" }
|
|
38511
|
-
adoptsFrom: NC_SEQUENCES_COLLECTION$1
|
|
38509
|
+
retention: { none: "a row goes only when an admin deletes it; bounded by the sequence set a human is willing to author" }
|
|
38512
38510
|
},
|
|
38513
38511
|
/**
|
|
38514
38512
|
* Per-rule AI section: whether and how a rule narrates itself.
|
|
@@ -38520,8 +38518,7 @@ var ncRecordKinds = require_clip_model_registry.defineRecordKinds({
|
|
|
38520
38518
|
writeTrigger: "an operator switches the AI section of a notification rule on or off, or edits its prompt / word cap / language",
|
|
38521
38519
|
device: "none",
|
|
38522
38520
|
at: "none",
|
|
38523
|
-
retention: { none: "a row goes when the operator clears the section or deletes the rule; bounded by the rule set a human is willing to author" }
|
|
38524
|
-
adoptsFrom: NC_RULE_AI_COLLECTION$1
|
|
38521
|
+
retention: { none: "a row goes when the operator clears the section or deletes the rule; bounded by the rule set a human is willing to author" }
|
|
38525
38522
|
},
|
|
38526
38523
|
/**
|
|
38527
38524
|
* Calendar-events rules. One row per rule, id = rule UUID.
|
|
@@ -38536,8 +38533,7 @@ var ncRecordKinds = require_clip_model_registry.defineRecordKinds({
|
|
|
38536
38533
|
name: "enabled",
|
|
38537
38534
|
fields: ["enabled"]
|
|
38538
38535
|
}],
|
|
38539
|
-
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
38540
|
-
adoptsFrom: NC_CALENDAR_EVENTS_RULES_COLLECTION$1
|
|
38536
|
+
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
38541
38537
|
},
|
|
38542
38538
|
/**
|
|
38543
38539
|
* Multi-camera digest rules. One row per rule, id = rule UUID.
|
|
@@ -38552,8 +38548,7 @@ var ncRecordKinds = require_clip_model_registry.defineRecordKinds({
|
|
|
38552
38548
|
name: "enabled",
|
|
38553
38549
|
fields: ["enabled"]
|
|
38554
38550
|
}],
|
|
38555
|
-
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
38556
|
-
adoptsFrom: NC_SUMMARY_RULES_COLLECTION$1
|
|
38551
|
+
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
38557
38552
|
},
|
|
38558
38553
|
/**
|
|
38559
38554
|
* Timelapse rules. One row per rule, id = rule UUID.
|
|
@@ -38569,8 +38564,7 @@ var ncRecordKinds = require_clip_model_registry.defineRecordKinds({
|
|
|
38569
38564
|
name: "enabled",
|
|
38570
38565
|
fields: ["enabled"]
|
|
38571
38566
|
}],
|
|
38572
|
-
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
38573
|
-
adoptsFrom: NC_TIMELAPSE_RULES_COLLECTION$1
|
|
38567
|
+
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
38574
38568
|
},
|
|
38575
38569
|
/**
|
|
38576
38570
|
* Notification rules — the main rule set. One row per rule, id = rule UUID.
|
|
@@ -38586,8 +38580,7 @@ var ncRecordKinds = require_clip_model_registry.defineRecordKinds({
|
|
|
38586
38580
|
name: "delivery",
|
|
38587
38581
|
fields: ["delivery"]
|
|
38588
38582
|
}],
|
|
38589
|
-
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
38590
|
-
adoptsFrom: NC_RULES_COLLECTION$1
|
|
38583
|
+
retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
|
|
38591
38584
|
}
|
|
38592
38585
|
});
|
|
38593
38586
|
//#endregion
|
|
@@ -67474,7 +67467,7 @@ async function settleReplaced(deps, sample, replacement, dryRun) {
|
|
|
67474
67467
|
});
|
|
67475
67468
|
return "old-removal-retried";
|
|
67476
67469
|
}
|
|
67477
|
-
function tally$
|
|
67470
|
+
function tally$2(samples, outcomes, dryRun) {
|
|
67478
67471
|
const count = (kind) => outcomes.filter((o) => o.outcome === kind).length;
|
|
67479
67472
|
const refusals = [];
|
|
67480
67473
|
for (const o of outcomes) if (o.outcome === "refused" && o.reason !== void 0) refusals.push({
|
|
@@ -67546,7 +67539,7 @@ async function reenrolPhotoSamples(deps, options) {
|
|
|
67546
67539
|
outcomes.push(outcomeOf(sample, verdict));
|
|
67547
67540
|
await deps.pause(pacingMs);
|
|
67548
67541
|
}
|
|
67549
|
-
const result = tally$
|
|
67542
|
+
const result = tally$2(samples, outcomes, dryRun);
|
|
67550
67543
|
if (!dryRun && (result.reenrolled > 0 || result.oldRemovalRetried > 0)) await deps.refreshGallery();
|
|
67551
67544
|
const { outcomes: _perSample, ...summary } = result;
|
|
67552
67545
|
deps.logger.info("face photo re-enrol finished", { meta: { ...summary } });
|
|
@@ -78963,24 +78956,11 @@ function createDiscardJob(deps) {
|
|
|
78963
78956
|
});
|
|
78964
78957
|
await sleep(0);
|
|
78965
78958
|
}
|
|
78966
|
-
|
|
78959
|
+
const retrainIds = (await deps.retrainFrameLedger.query({})).filter((row) => row.data["locationId"] === locationId).map((row) => row.id);
|
|
78960
|
+
for (let i = 0; i < retrainIds.length; i += MEDIA_PAGE) {
|
|
78967
78961
|
if (cancelRequested) return owners;
|
|
78968
|
-
const
|
|
78969
|
-
|
|
78970
|
-
columns: ["id"],
|
|
78971
|
-
filter: {
|
|
78972
|
-
where: { locationId },
|
|
78973
|
-
limit: MEDIA_PAGE
|
|
78974
|
-
}
|
|
78975
|
-
});
|
|
78976
|
-
if (rows.length === 0) break;
|
|
78977
|
-
for (const row of rows) {
|
|
78978
|
-
await deps.store.delete.mutate({
|
|
78979
|
-
collection: RETRAIN_FRAMES_COLLECTION,
|
|
78980
|
-
key: row.id
|
|
78981
|
-
});
|
|
78982
|
-
deleted += 1;
|
|
78983
|
-
}
|
|
78962
|
+
const page = retrainIds.slice(i, i + MEDIA_PAGE);
|
|
78963
|
+
deleted += await deps.retrainFrameLedger.deleteByIds(page);
|
|
78984
78964
|
report({
|
|
78985
78965
|
dbRowsDeleted: deleted,
|
|
78986
78966
|
detail: `deleted ${deleted} media/retrain rows`
|
|
@@ -79032,6 +79012,383 @@ function createDiscardJob(deps) {
|
|
|
79032
79012
|
};
|
|
79033
79013
|
}
|
|
79034
79014
|
//#endregion
|
|
79015
|
+
//#region src/pipeline-analytics/retention/media-forget-actions.ts
|
|
79016
|
+
/**
|
|
79017
|
+
* The operator door for the NARROW media-row forget (`media-row-forget.ts`).
|
|
79018
|
+
*
|
|
79019
|
+
* It forgets `pipeline-analytics:media` rows stamped with ONE location whose
|
|
79020
|
+
* file is provably gone, and nothing else: no event, track, object-event,
|
|
79021
|
+
* face, plate or identity row is read for deletion, ever. Contrast
|
|
79022
|
+
* `storage.discardLocation`, which wipes the disk AND cascades owners.
|
|
79023
|
+
*
|
|
79024
|
+
* `dryRun` defaults to `true`: an unstated intent destroys nothing. Applying
|
|
79025
|
+
* also requires `expectAbsent` — the `absent` count a dry run just reported —
|
|
79026
|
+
* so the forget only ever deletes a population a human has already read.
|
|
79027
|
+
*/
|
|
79028
|
+
/** Hard ceiling on rows one call examines — the call is synchronous. */
|
|
79029
|
+
var MEDIA_FORGET_MAX_ROWS = 5e4;
|
|
79030
|
+
var MediaForgetInputSchema = require_clip_model_registry.object({
|
|
79031
|
+
locationId: require_clip_model_registry.string().min(1),
|
|
79032
|
+
/** Restrict to these cameras. Omitted = every row stamped with the location. */
|
|
79033
|
+
deviceIds: require_clip_model_registry.array(require_clip_model_registry.number().int()).min(1).optional(),
|
|
79034
|
+
dryRun: require_clip_model_registry.boolean().default(true),
|
|
79035
|
+
/**
|
|
79036
|
+
* Required when `dryRun` is false: the `totals.absent` of the dry run. A
|
|
79037
|
+
* different count at apply time refuses the whole call before any delete.
|
|
79038
|
+
*/
|
|
79039
|
+
expectAbsent: require_clip_model_registry.number().int().nonnegative().optional(),
|
|
79040
|
+
/** Rows examined in this call. Default and max {@link MEDIA_FORGET_MAX_ROWS}. */
|
|
79041
|
+
maxRows: require_clip_model_registry.number().int().min(1).max(MEDIA_FORGET_MAX_ROWS).optional(),
|
|
79042
|
+
/** Detailed rows returned per verdict. Default 20. */
|
|
79043
|
+
sampleSize: require_clip_model_registry.number().int().min(0).max(500).optional()
|
|
79044
|
+
});
|
|
79045
|
+
var VerdictCountsSchema = require_clip_model_registry.object({
|
|
79046
|
+
rows: require_clip_model_registry.number(),
|
|
79047
|
+
/** File answered ENOENT — the only verdict that may be forgotten. */
|
|
79048
|
+
absent: require_clip_model_registry.number(),
|
|
79049
|
+
/** File is there: relocation is incomplete. Never forgotten. */
|
|
79050
|
+
present: require_clip_model_registry.number(),
|
|
79051
|
+
/** The stat failed with any errno other than ENOENT. Never forgotten. */
|
|
79052
|
+
error: require_clip_model_registry.number(),
|
|
79053
|
+
/** No path, a path escaping the root, or a retention-exempt owner. Never forgotten. */
|
|
79054
|
+
kept: require_clip_model_registry.number(),
|
|
79055
|
+
/** Rows actually deleted (0 on a dry run). */
|
|
79056
|
+
deleted: require_clip_model_registry.number()
|
|
79057
|
+
});
|
|
79058
|
+
var MediaForgetSampleSchema = require_clip_model_registry.object({
|
|
79059
|
+
mediaId: require_clip_model_registry.number(),
|
|
79060
|
+
deviceId: require_clip_model_registry.number(),
|
|
79061
|
+
kind: require_clip_model_registry.string(),
|
|
79062
|
+
ownerKind: require_clip_model_registry.string(),
|
|
79063
|
+
ownerId: require_clip_model_registry.string(),
|
|
79064
|
+
path: require_clip_model_registry.string(),
|
|
79065
|
+
/** The errno (error) or the reason (kept); absent otherwise. */
|
|
79066
|
+
reason: require_clip_model_registry.string().optional()
|
|
79067
|
+
});
|
|
79068
|
+
var MediaForgetResultSchema = require_clip_model_registry.object({
|
|
79069
|
+
locationId: require_clip_model_registry.string(),
|
|
79070
|
+
dryRun: require_clip_model_registry.boolean(),
|
|
79071
|
+
root: require_clip_model_registry.string(),
|
|
79072
|
+
/** Rows of this location that were not examined because `maxRows` was hit. */
|
|
79073
|
+
truncated: require_clip_model_registry.boolean(),
|
|
79074
|
+
totals: VerdictCountsSchema,
|
|
79075
|
+
byDevice: require_clip_model_registry.array(VerdictCountsSchema.extend({ deviceId: require_clip_model_registry.number() })),
|
|
79076
|
+
byKind: require_clip_model_registry.array(VerdictCountsSchema.extend({ kind: require_clip_model_registry.string() })),
|
|
79077
|
+
/** Every id the call deletes (dry run: would delete). The exact list. */
|
|
79078
|
+
absentIds: require_clip_model_registry.array(require_clip_model_registry.number()),
|
|
79079
|
+
samples: require_clip_model_registry.object({
|
|
79080
|
+
absent: require_clip_model_registry.array(MediaForgetSampleSchema),
|
|
79081
|
+
present: require_clip_model_registry.array(MediaForgetSampleSchema),
|
|
79082
|
+
error: require_clip_model_registry.array(MediaForgetSampleSchema),
|
|
79083
|
+
kept: require_clip_model_registry.array(MediaForgetSampleSchema)
|
|
79084
|
+
}),
|
|
79085
|
+
/** Absent at classification, no longer ENOENT at delete time: kept. */
|
|
79086
|
+
changedBeforeDelete: require_clip_model_registry.number()
|
|
79087
|
+
});
|
|
79088
|
+
var mediaForgetActions = require_clip_model_registry.defineCustomActions({ "storage.forgetMissingMedia": require_clip_model_registry.customAction(MediaForgetInputSchema, MediaForgetResultSchema, {
|
|
79089
|
+
kind: "mutation",
|
|
79090
|
+
auth: "admin"
|
|
79091
|
+
}) });
|
|
79092
|
+
//#endregion
|
|
79093
|
+
//#region src/pipeline-analytics/retention/media-row-forget.ts
|
|
79094
|
+
/**
|
|
79095
|
+
* The NARROW media-row forget: delete `pipeline-analytics:media` rows stamped
|
|
79096
|
+
* with ONE storage location whose file is provably gone — and nothing else.
|
|
79097
|
+
*
|
|
79098
|
+
* Why it exists: a location whose bytes were lost keeps rows naming it, and a
|
|
79099
|
+
* cascading cleanup is not an acceptable way to clear them. `reconcileFromDisk`
|
|
79100
|
+
* once cascaded ~132 k events away from a read it did not double-check. Here:
|
|
79101
|
+
*
|
|
79102
|
+
* - **Only media rows.** No event, track, object-event, face, plate or
|
|
79103
|
+
* identity row is deleted. A parent keeps existing with that media kind
|
|
79104
|
+
* missing — which is exactly what it already serves (`getBytesById` answers
|
|
79105
|
+
* `null` for a missing row as it does for a missing blob).
|
|
79106
|
+
* - **Only `ENOENT` is absence (D49).** Each row's file is `stat`ed under the
|
|
79107
|
+
* location's own root on THIS node. Any other errno keeps the row and is
|
|
79108
|
+
* reported; a present file keeps the row and is reported (relocation is
|
|
79109
|
+
* incomplete). A row with no path, a path that escapes the root, or a
|
|
79110
|
+
* retention-exempt owner (the enrolled gallery) is kept.
|
|
79111
|
+
* - **Only rows stamped with the id.** `locationId IS NULL` rows mean
|
|
79112
|
+
* "wherever eventMedia points now" and are never selected.
|
|
79113
|
+
* - **Only a location nothing writes to.** An active (writable) location is
|
|
79114
|
+
* refused: new rows could land while the walk runs.
|
|
79115
|
+
* - **Dry run first, then the same count.** Applying requires
|
|
79116
|
+
* `expectAbsent`; the whole population is classified before any delete,
|
|
79117
|
+
* and a different absent count refuses the call with nothing written.
|
|
79118
|
+
* Each id is re-`stat`ed right before its batch is deleted, and the delete
|
|
79119
|
+
* predicate repeats `locationId`, so a row repointed in between survives.
|
|
79120
|
+
*
|
|
79121
|
+
* Occupancy needs no separate bookkeeping: `storage-occupancy` sums
|
|
79122
|
+
* `sizeBytes WHERE locationId = ?` on demand, so the row IS the accounting.
|
|
79123
|
+
*/
|
|
79124
|
+
var PAGE_SIZE$1 = 200;
|
|
79125
|
+
var DELETE_BATCH = 200;
|
|
79126
|
+
var DEFAULT_SAMPLE_SIZE = 20;
|
|
79127
|
+
function errnoOf(err) {
|
|
79128
|
+
if (err instanceof Error && "code" in err && typeof err.code === "string") return err.code;
|
|
79129
|
+
return err instanceof Error ? err.message : String(err);
|
|
79130
|
+
}
|
|
79131
|
+
function emptyCounts() {
|
|
79132
|
+
return {
|
|
79133
|
+
rows: 0,
|
|
79134
|
+
absent: 0,
|
|
79135
|
+
present: 0,
|
|
79136
|
+
error: 0,
|
|
79137
|
+
kept: 0,
|
|
79138
|
+
deleted: 0
|
|
79139
|
+
};
|
|
79140
|
+
}
|
|
79141
|
+
function tally$1(counts, verdict) {
|
|
79142
|
+
const rows = counts.rows + 1;
|
|
79143
|
+
switch (verdict) {
|
|
79144
|
+
case "absent": return {
|
|
79145
|
+
...counts,
|
|
79146
|
+
rows,
|
|
79147
|
+
absent: counts.absent + 1
|
|
79148
|
+
};
|
|
79149
|
+
case "present": return {
|
|
79150
|
+
...counts,
|
|
79151
|
+
rows,
|
|
79152
|
+
present: counts.present + 1
|
|
79153
|
+
};
|
|
79154
|
+
case "error": return {
|
|
79155
|
+
...counts,
|
|
79156
|
+
rows,
|
|
79157
|
+
error: counts.error + 1
|
|
79158
|
+
};
|
|
79159
|
+
case "kept": return {
|
|
79160
|
+
...counts,
|
|
79161
|
+
rows,
|
|
79162
|
+
kept: counts.kept + 1
|
|
79163
|
+
};
|
|
79164
|
+
}
|
|
79165
|
+
}
|
|
79166
|
+
/** The file's absolute path under `root`, or `null` when it would escape it. */
|
|
79167
|
+
function resolveUnder(root, relativePath) {
|
|
79168
|
+
const base = node_path.default.resolve(root);
|
|
79169
|
+
const full = node_path.default.resolve(base, relativePath);
|
|
79170
|
+
return full.startsWith(base + node_path.default.sep) ? full : null;
|
|
79171
|
+
}
|
|
79172
|
+
function toSample(id, data) {
|
|
79173
|
+
return {
|
|
79174
|
+
mediaId: Number(id),
|
|
79175
|
+
deviceId: Number(data["deviceId"] ?? 0),
|
|
79176
|
+
kind: String(data["kind"] ?? ""),
|
|
79177
|
+
ownerKind: String(data["ownerKind"] ?? ""),
|
|
79178
|
+
ownerId: String(data["ownerId"] ?? ""),
|
|
79179
|
+
path: String(data["path"] ?? "")
|
|
79180
|
+
};
|
|
79181
|
+
}
|
|
79182
|
+
async function probe(stat, absolutePath) {
|
|
79183
|
+
try {
|
|
79184
|
+
await stat(absolutePath);
|
|
79185
|
+
return { verdict: "present" };
|
|
79186
|
+
} catch (err) {
|
|
79187
|
+
const code = errnoOf(err);
|
|
79188
|
+
return code === "ENOENT" ? { verdict: "absent" } : {
|
|
79189
|
+
verdict: "error",
|
|
79190
|
+
reason: code
|
|
79191
|
+
};
|
|
79192
|
+
}
|
|
79193
|
+
}
|
|
79194
|
+
async function classify(stat, root, id, data) {
|
|
79195
|
+
const sample = toSample(id, data);
|
|
79196
|
+
const ownerKind = sample.ownerKind;
|
|
79197
|
+
if (isMediaOwnerKind(ownerKind) && isRetentionExemptOwnerKind(ownerKind)) return {
|
|
79198
|
+
key: id,
|
|
79199
|
+
sample: {
|
|
79200
|
+
...sample,
|
|
79201
|
+
reason: "retention-exempt owner"
|
|
79202
|
+
},
|
|
79203
|
+
verdict: "kept",
|
|
79204
|
+
absolutePath: null
|
|
79205
|
+
};
|
|
79206
|
+
if (sample.path.length === 0) return {
|
|
79207
|
+
key: id,
|
|
79208
|
+
sample: {
|
|
79209
|
+
...sample,
|
|
79210
|
+
reason: "no path"
|
|
79211
|
+
},
|
|
79212
|
+
verdict: "kept",
|
|
79213
|
+
absolutePath: null
|
|
79214
|
+
};
|
|
79215
|
+
const absolutePath = resolveUnder(root, sample.path);
|
|
79216
|
+
if (absolutePath === null) return {
|
|
79217
|
+
key: id,
|
|
79218
|
+
sample: {
|
|
79219
|
+
...sample,
|
|
79220
|
+
reason: "path escapes the root"
|
|
79221
|
+
},
|
|
79222
|
+
verdict: "kept",
|
|
79223
|
+
absolutePath
|
|
79224
|
+
};
|
|
79225
|
+
const result = await probe(stat, absolutePath);
|
|
79226
|
+
return {
|
|
79227
|
+
key: id,
|
|
79228
|
+
sample: result.reason === void 0 ? sample : {
|
|
79229
|
+
...sample,
|
|
79230
|
+
reason: result.reason
|
|
79231
|
+
},
|
|
79232
|
+
verdict: result.verdict,
|
|
79233
|
+
absolutePath
|
|
79234
|
+
};
|
|
79235
|
+
}
|
|
79236
|
+
function resolveTarget(locations, locationId, localNodeId) {
|
|
79237
|
+
const location = locations.find((row) => row.id === locationId);
|
|
79238
|
+
if (!location) throw new Error(`forgetMissingMedia refused: unknown location ${locationId}`);
|
|
79239
|
+
if (location.type !== "eventMedia") throw new Error(`forgetMissingMedia refused: ${locationId} is ${location.type}, pipeline-analytics owns eventMedia rows only`);
|
|
79240
|
+
if (require_clip_model_registry.mayWriteToLocation(location)) throw new Error(`forgetMissingMedia refused: ${locationId} is still a write target — set it to drain, readonly or disabled first`);
|
|
79241
|
+
const nodeId = location.nodeId === void 0 || location.nodeId === "" ? "hub" : location.nodeId;
|
|
79242
|
+
if (nodeId !== localNodeId) throw new Error(`forgetMissingMedia refused: ${locationId} lives on node ${nodeId}, this process is ${localNodeId}`);
|
|
79243
|
+
return {
|
|
79244
|
+
location,
|
|
79245
|
+
root: locationBasePath(location)
|
|
79246
|
+
};
|
|
79247
|
+
}
|
|
79248
|
+
/**
|
|
79249
|
+
* Classify every row stamped with `input.locationId` (bounded by `maxRows`),
|
|
79250
|
+
* and — only when `dryRun` is false and the absent count equals
|
|
79251
|
+
* `expectAbsent` — delete the absent ones, per camera, in batches.
|
|
79252
|
+
*/
|
|
79253
|
+
async function forgetMissingMediaRows(deps, rawInput) {
|
|
79254
|
+
const input = MediaForgetInputSchema.parse(rawInput);
|
|
79255
|
+
if (!input.dryRun && input.expectAbsent === void 0) throw new Error("forgetMissingMedia refused: an apply needs expectAbsent (the dry run’s totals.absent)");
|
|
79256
|
+
const stat = deps.stat ?? ((p) => node_fs.promises.stat(p));
|
|
79257
|
+
const pace = deps.pace ?? (() => new Promise((resolve) => setImmediate(resolve)));
|
|
79258
|
+
const maxRows = input.maxRows ?? MEDIA_FORGET_MAX_ROWS;
|
|
79259
|
+
const sampleSize = input.sampleSize ?? DEFAULT_SAMPLE_SIZE;
|
|
79260
|
+
const { root } = resolveTarget(await deps.listLocations(), input.locationId, deps.localNodeId);
|
|
79261
|
+
const classified = [];
|
|
79262
|
+
let cursor = 0;
|
|
79263
|
+
let truncated = false;
|
|
79264
|
+
for (;;) {
|
|
79265
|
+
const rows = await deps.store.query.query({
|
|
79266
|
+
collection: MEDIA_COLLECTION,
|
|
79267
|
+
columns: [
|
|
79268
|
+
"id",
|
|
79269
|
+
"deviceId",
|
|
79270
|
+
"ownerKind",
|
|
79271
|
+
"ownerId",
|
|
79272
|
+
"kind",
|
|
79273
|
+
"path"
|
|
79274
|
+
],
|
|
79275
|
+
filter: {
|
|
79276
|
+
where: { locationId: input.locationId },
|
|
79277
|
+
...input.deviceIds ? { whereIn: { deviceId: [...input.deviceIds] } } : {},
|
|
79278
|
+
whereBetween: { id: [cursor + 1, Number.MAX_SAFE_INTEGER] },
|
|
79279
|
+
orderBy: {
|
|
79280
|
+
field: "id",
|
|
79281
|
+
direction: "asc"
|
|
79282
|
+
},
|
|
79283
|
+
limit: PAGE_SIZE$1
|
|
79284
|
+
}
|
|
79285
|
+
});
|
|
79286
|
+
if (rows.length === 0) break;
|
|
79287
|
+
for (const row of rows) {
|
|
79288
|
+
if (classified.length >= maxRows) {
|
|
79289
|
+
truncated = true;
|
|
79290
|
+
break;
|
|
79291
|
+
}
|
|
79292
|
+
classified.push(await classify(stat, root, row.id, row.data));
|
|
79293
|
+
cursor = Math.max(cursor, Number(row.id));
|
|
79294
|
+
}
|
|
79295
|
+
if (truncated || rows.length < PAGE_SIZE$1) break;
|
|
79296
|
+
await pace();
|
|
79297
|
+
}
|
|
79298
|
+
const absentRows = classified.filter((c) => c.verdict === "absent");
|
|
79299
|
+
const deletedIds = /* @__PURE__ */ new Set();
|
|
79300
|
+
let changedBeforeDelete = 0;
|
|
79301
|
+
if (!input.dryRun) {
|
|
79302
|
+
if (absentRows.length !== input.expectAbsent) throw new Error(`forgetMissingMedia refused: ${String(absentRows.length)} rows are absent now, expectAbsent was ${String(input.expectAbsent)} — run the dry run again; nothing was deleted`);
|
|
79303
|
+
const byDevice = /* @__PURE__ */ new Map();
|
|
79304
|
+
for (const row of absentRows) byDevice.set(row.sample.deviceId, [...byDevice.get(row.sample.deviceId) ?? [], row]);
|
|
79305
|
+
for (const [deviceId, rows] of byDevice) for (let at = 0; at < rows.length; at += DELETE_BATCH) {
|
|
79306
|
+
const batch = [];
|
|
79307
|
+
for (const row of rows.slice(at, at + DELETE_BATCH)) if ((row.absolutePath === null ? null : await probe(stat, row.absolutePath))?.verdict === "absent") batch.push(row);
|
|
79308
|
+
else changedBeforeDelete += 1;
|
|
79309
|
+
if (batch.length === 0) continue;
|
|
79310
|
+
const { deleted } = await deps.store.deleteWhere.mutate({
|
|
79311
|
+
collection: MEDIA_COLLECTION,
|
|
79312
|
+
filter: {
|
|
79313
|
+
where: {
|
|
79314
|
+
locationId: input.locationId,
|
|
79315
|
+
deviceId
|
|
79316
|
+
},
|
|
79317
|
+
whereIn: { id: batch.map((row) => row.key) }
|
|
79318
|
+
}
|
|
79319
|
+
});
|
|
79320
|
+
for (const row of batch) deletedIds.add(row.sample.mediaId);
|
|
79321
|
+
deps.logger.info("forgetMissingMedia: forgot media rows whose file is gone", {
|
|
79322
|
+
tags: { deviceId },
|
|
79323
|
+
meta: {
|
|
79324
|
+
locationId: input.locationId,
|
|
79325
|
+
batch: batch.length,
|
|
79326
|
+
deleted,
|
|
79327
|
+
firstId: batch[0]?.sample.mediaId,
|
|
79328
|
+
lastId: batch[batch.length - 1]?.sample.mediaId
|
|
79329
|
+
}
|
|
79330
|
+
});
|
|
79331
|
+
if (deleted !== batch.length) deps.logger.warn("forgetMissingMedia: a batch deleted fewer rows than it named", {
|
|
79332
|
+
tags: { deviceId },
|
|
79333
|
+
meta: {
|
|
79334
|
+
locationId: input.locationId,
|
|
79335
|
+
batch: batch.length,
|
|
79336
|
+
deleted
|
|
79337
|
+
}
|
|
79338
|
+
});
|
|
79339
|
+
await pace();
|
|
79340
|
+
}
|
|
79341
|
+
}
|
|
79342
|
+
let totals = emptyCounts();
|
|
79343
|
+
const perDevice = /* @__PURE__ */ new Map();
|
|
79344
|
+
const perKind = /* @__PURE__ */ new Map();
|
|
79345
|
+
for (const row of classified) {
|
|
79346
|
+
const isDeleted = deletedIds.has(row.sample.mediaId);
|
|
79347
|
+
const bump = (c) => {
|
|
79348
|
+
const next = tally$1(c, row.verdict);
|
|
79349
|
+
return isDeleted ? {
|
|
79350
|
+
...next,
|
|
79351
|
+
deleted: next.deleted + 1
|
|
79352
|
+
} : next;
|
|
79353
|
+
};
|
|
79354
|
+
totals = bump(totals);
|
|
79355
|
+
perDevice.set(row.sample.deviceId, bump(perDevice.get(row.sample.deviceId) ?? emptyCounts()));
|
|
79356
|
+
perKind.set(row.sample.kind, bump(perKind.get(row.sample.kind) ?? emptyCounts()));
|
|
79357
|
+
}
|
|
79358
|
+
const samplesOf = (verdict) => classified.filter((c) => c.verdict === verdict).slice(0, sampleSize).map((c) => c.sample);
|
|
79359
|
+
for (const [deviceId, counts] of perDevice) deps.logger.info("forgetMissingMedia: per-camera verdict", {
|
|
79360
|
+
tags: { deviceId },
|
|
79361
|
+
meta: {
|
|
79362
|
+
locationId: input.locationId,
|
|
79363
|
+
dryRun: input.dryRun,
|
|
79364
|
+
...counts
|
|
79365
|
+
}
|
|
79366
|
+
});
|
|
79367
|
+
return {
|
|
79368
|
+
locationId: input.locationId,
|
|
79369
|
+
dryRun: input.dryRun,
|
|
79370
|
+
root,
|
|
79371
|
+
truncated,
|
|
79372
|
+
totals,
|
|
79373
|
+
byDevice: [...perDevice].map(([deviceId, c]) => ({
|
|
79374
|
+
deviceId,
|
|
79375
|
+
...c
|
|
79376
|
+
})),
|
|
79377
|
+
byKind: [...perKind].map(([kind, c]) => ({
|
|
79378
|
+
kind,
|
|
79379
|
+
...c
|
|
79380
|
+
})),
|
|
79381
|
+
absentIds: absentRows.map((r) => r.sample.mediaId),
|
|
79382
|
+
samples: {
|
|
79383
|
+
absent: samplesOf("absent"),
|
|
79384
|
+
present: samplesOf("present"),
|
|
79385
|
+
error: samplesOf("error"),
|
|
79386
|
+
kept: samplesOf("kept")
|
|
79387
|
+
},
|
|
79388
|
+
changedBeforeDelete
|
|
79389
|
+
};
|
|
79390
|
+
}
|
|
79391
|
+
//#endregion
|
|
79035
79392
|
//#region src/pipeline-analytics/retention/media-reclaim.ts
|
|
79036
79393
|
var MEDIA_RECLAIM_SCOPES = ["motion-still", "track-filmstrip"];
|
|
79037
79394
|
var DEFAULT_MEDIA_RECLAIM_LIMITS = {
|
|
@@ -93359,17 +93716,8 @@ function rowIsGallery(data) {
|
|
|
93359
93716
|
* D295 calls "a guard nobody believes".
|
|
93360
93717
|
*/
|
|
93361
93718
|
function collectionsFor(mode) {
|
|
93362
|
-
if (mode === "gallery") return [{
|
|
93363
|
-
|
|
93364
|
-
order: "timestamp"
|
|
93365
|
-
}];
|
|
93366
|
-
return [{
|
|
93367
|
-
name: MEDIA_COLLECTION,
|
|
93368
|
-
order: "timestamp"
|
|
93369
|
-
}, {
|
|
93370
|
-
name: RETRAIN_FRAMES_COLLECTION,
|
|
93371
|
-
order: "copiedAt"
|
|
93372
|
-
}];
|
|
93719
|
+
if (mode === "gallery") return [{ lane: "media" }];
|
|
93720
|
+
return [{ lane: "media" }, { lane: "retrainFrames" }];
|
|
93373
93721
|
}
|
|
93374
93722
|
/**
|
|
93375
93723
|
* Does this pass act on this row at all?
|
|
@@ -93454,7 +93802,7 @@ var MediaRelocateEngine = class {
|
|
|
93454
93802
|
async countUnstamped() {
|
|
93455
93803
|
try {
|
|
93456
93804
|
const media = await this.countUnstampedIn(MEDIA_COLLECTION);
|
|
93457
|
-
const retrainFrames = await this.
|
|
93805
|
+
const retrainFrames = await this.countUnstampedRetrainFrames();
|
|
93458
93806
|
return {
|
|
93459
93807
|
media,
|
|
93460
93808
|
retrainFrames,
|
|
@@ -93506,6 +93854,28 @@ var MediaRelocateEngine = class {
|
|
|
93506
93854
|
};
|
|
93507
93855
|
}
|
|
93508
93856
|
}
|
|
93857
|
+
/**
|
|
93858
|
+
* The retrain lane's unstamped population. The ledger's records `where` is
|
|
93859
|
+
* equality-only (a `null` predicate is dropped, which would count the whole
|
|
93860
|
+
* dataset), so the rows are read and judged by {@link rowIsUnstamped} — the
|
|
93861
|
+
* dataset is operator-selected and small.
|
|
93862
|
+
*/
|
|
93863
|
+
async countUnstampedRetrainFrames() {
|
|
93864
|
+
const rows = (await this.deps.retrainFrameLedger.query({})).filter((row) => rowIsUnstamped(row.data)).length;
|
|
93865
|
+
return {
|
|
93866
|
+
present: rows > 0,
|
|
93867
|
+
rows
|
|
93868
|
+
};
|
|
93869
|
+
}
|
|
93870
|
+
/** Every row of one lane, oldest-first. */
|
|
93871
|
+
rowsOf(collection) {
|
|
93872
|
+
return collection.lane === "media" ? this.pageRows(MEDIA_COLLECTION, "timestamp") : this.retrainFrameRows();
|
|
93873
|
+
}
|
|
93874
|
+
/** The retrain-frame ledger, read whole and yielded oldest copy first. */
|
|
93875
|
+
async *retrainFrameRows() {
|
|
93876
|
+
const ordered = (await this.deps.retrainFrameLedger.query({})).toSorted((a, b) => Number(a.data["copiedAt"] ?? 0) - Number(b.data["copiedAt"] ?? 0));
|
|
93877
|
+
for (const row of ordered) yield row;
|
|
93878
|
+
}
|
|
93509
93879
|
start(input) {
|
|
93510
93880
|
for (const j of this.jobs.values()) if (j.state === "running") throw new Error(`a media relocation is already running (${j.jobId})`);
|
|
93511
93881
|
this.cancelCensuses("a relocate pass started");
|
|
@@ -93609,22 +93979,22 @@ var MediaRelocateEngine = class {
|
|
|
93609
93979
|
let sealable = 0;
|
|
93610
93980
|
for (const collection of collectionsFor(mode)) {
|
|
93611
93981
|
if (stop()) return null;
|
|
93612
|
-
sealable += await this.deps.store.count.query({
|
|
93613
|
-
collection:
|
|
93982
|
+
sealable += collection.lane === "media" ? await this.deps.store.count.query({
|
|
93983
|
+
collection: MEDIA_COLLECTION,
|
|
93614
93984
|
filter: { where: { [LOCATION_ID_FIELD]: null } }
|
|
93615
|
-
});
|
|
93985
|
+
}) : (await this.countUnstampedRetrainFrames()).rows ?? 0;
|
|
93616
93986
|
}
|
|
93617
93987
|
return { rows: sealable };
|
|
93618
93988
|
}
|
|
93619
93989
|
let rows = 0;
|
|
93620
93990
|
for (const collection of collectionsFor(mode)) {
|
|
93621
|
-
const isRetrain = collection.
|
|
93622
|
-
for await (const row of this.
|
|
93991
|
+
const isRetrain = collection.lane === "retrainFrames";
|
|
93992
|
+
for await (const row of this.rowsOf(collection)) {
|
|
93623
93993
|
if (stop()) {
|
|
93624
93994
|
this.deps.logger.warn("media relocate: census stopped before it finished — reporting the residue as unknown, not as empty", { meta: {
|
|
93625
93995
|
mode,
|
|
93626
93996
|
toLocationId,
|
|
93627
|
-
collection: collection.
|
|
93997
|
+
collection: collection.lane
|
|
93628
93998
|
} });
|
|
93629
93999
|
return null;
|
|
93630
94000
|
}
|
|
@@ -93632,7 +94002,7 @@ var MediaRelocateEngine = class {
|
|
|
93632
94002
|
this.deps.logger.warn("media relocate: census exceeded its budget — this collection is larger than a walk can answer", { meta: {
|
|
93633
94003
|
mode,
|
|
93634
94004
|
toLocationId,
|
|
93635
|
-
collection: collection.
|
|
94005
|
+
collection: collection.lane,
|
|
93636
94006
|
rowsVisited: budget.rowsVisited,
|
|
93637
94007
|
rowBudget: MEDIA_CENSUS_ROW_BUDGET,
|
|
93638
94008
|
deadlineMs: MEDIA_CENSUS_DEADLINE_MS
|
|
@@ -93668,15 +94038,15 @@ var MediaRelocateEngine = class {
|
|
|
93668
94038
|
if (job.mode === "seal") {
|
|
93669
94039
|
for (const collection of collectionsFor(job.mode)) {
|
|
93670
94040
|
if (job.cancelRequested) break;
|
|
93671
|
-
await this.sealCollection(collection
|
|
94041
|
+
await this.sealCollection(collection, job);
|
|
93672
94042
|
}
|
|
93673
94043
|
job.state = job.cancelRequested ? "cancelled" : "done";
|
|
93674
94044
|
return;
|
|
93675
94045
|
}
|
|
93676
94046
|
for (const collection of collectionsFor(job.mode)) {
|
|
93677
94047
|
if (job.cancelRequested) break;
|
|
93678
|
-
const isRetrain = collection.
|
|
93679
|
-
for await (const row of this.
|
|
94048
|
+
const isRetrain = collection.lane === "retrainFrames";
|
|
94049
|
+
for await (const row of this.rowsOf(collection)) {
|
|
93680
94050
|
if (job.cancelRequested) break;
|
|
93681
94051
|
const data = row.data;
|
|
93682
94052
|
if (!selectsRow(job.mode, job.toLocationId, data, isRetrain, job.fromLocationId)) continue;
|
|
@@ -93688,7 +94058,7 @@ var MediaRelocateEngine = class {
|
|
|
93688
94058
|
}
|
|
93689
94059
|
try {
|
|
93690
94060
|
if (await this.samePhysicalLocation(from, job.toLocationId)) {
|
|
93691
|
-
await this.stamp(collection
|
|
94061
|
+
await this.stamp(collection, row.id, data, job.toLocationId);
|
|
93692
94062
|
continue;
|
|
93693
94063
|
}
|
|
93694
94064
|
const bytes = await this.deps.storage.read({
|
|
@@ -93704,7 +94074,7 @@ var MediaRelocateEngine = class {
|
|
|
93704
94074
|
location: job.toLocationId,
|
|
93705
94075
|
relativePath
|
|
93706
94076
|
})).length !== bytes.length) throw new Error(`relocated media size mismatch for ${relativePath}`);
|
|
93707
|
-
await this.stamp(collection
|
|
94077
|
+
await this.stamp(collection, row.id, data, job.toLocationId);
|
|
93708
94078
|
await this.deps.storage.delete({
|
|
93709
94079
|
location: from,
|
|
93710
94080
|
relativePath
|
|
@@ -93715,7 +94085,7 @@ var MediaRelocateEngine = class {
|
|
|
93715
94085
|
} catch (err) {
|
|
93716
94086
|
failedRows++;
|
|
93717
94087
|
this.deps.logger.debug("media relocate row failed", { meta: {
|
|
93718
|
-
collection: collection.
|
|
94088
|
+
collection: collection.lane,
|
|
93719
94089
|
key: row.id,
|
|
93720
94090
|
error: String(err)
|
|
93721
94091
|
} });
|
|
@@ -93788,7 +94158,17 @@ var MediaRelocateEngine = class {
|
|
|
93788
94158
|
* reports a completed seal having stamped nothing. Zero unstamped rows is the
|
|
93789
94159
|
* single value the migration gate reads as "safe to cut over".
|
|
93790
94160
|
*/
|
|
93791
|
-
async sealCollection(
|
|
94161
|
+
async sealCollection(lane, job) {
|
|
94162
|
+
if (lane.lane === "retrainFrames") {
|
|
94163
|
+
for (const row of await this.deps.retrainFrameLedger.query({})) {
|
|
94164
|
+
if (job.cancelRequested) return;
|
|
94165
|
+
if (!rowIsUnstamped(row.data)) continue;
|
|
94166
|
+
await this.deps.retrainFrameLedger.update(row.id, { [LOCATION_ID_FIELD]: job.toLocationId });
|
|
94167
|
+
job.filesMoved += 1;
|
|
94168
|
+
}
|
|
94169
|
+
return;
|
|
94170
|
+
}
|
|
94171
|
+
const collection = MEDIA_COLLECTION;
|
|
93792
94172
|
for (;;) {
|
|
93793
94173
|
if (job.cancelRequested) return;
|
|
93794
94174
|
const batch = await this.deps.store.query.query({
|
|
@@ -93809,9 +94189,13 @@ var MediaRelocateEngine = class {
|
|
|
93809
94189
|
job.filesMoved += updated;
|
|
93810
94190
|
}
|
|
93811
94191
|
}
|
|
93812
|
-
async stamp(
|
|
94192
|
+
async stamp(lane, key, data, locationId) {
|
|
94193
|
+
if (lane.lane === "retrainFrames") {
|
|
94194
|
+
await this.deps.retrainFrameLedger.update(key, { [LOCATION_ID_FIELD]: locationId });
|
|
94195
|
+
return;
|
|
94196
|
+
}
|
|
93813
94197
|
await this.deps.store.set.mutate({
|
|
93814
|
-
collection,
|
|
94198
|
+
collection: MEDIA_COLLECTION,
|
|
93815
94199
|
key,
|
|
93816
94200
|
value: {
|
|
93817
94201
|
...data,
|
|
@@ -102258,6 +102642,18 @@ function decodeCursorMap(raw) {
|
|
|
102258
102642
|
function nonNegativeInt(value) {
|
|
102259
102643
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : null;
|
|
102260
102644
|
}
|
|
102645
|
+
function decodeScopeCursors(raw) {
|
|
102646
|
+
const out = {};
|
|
102647
|
+
if (raw !== null && typeof raw === "object") for (const [key, cursor] of Object.entries({ ...raw })) {
|
|
102648
|
+
if (typeof cursor === "string" && cursor.length > 0) {
|
|
102649
|
+
out[key] = cursor;
|
|
102650
|
+
continue;
|
|
102651
|
+
}
|
|
102652
|
+
const parsed = nonNegativeInt(cursor);
|
|
102653
|
+
if (parsed !== null) out[key] = parsed;
|
|
102654
|
+
}
|
|
102655
|
+
return out;
|
|
102656
|
+
}
|
|
102261
102657
|
/** Read a stored row back into a cursor map, ignoring anything unreadable. */
|
|
102262
102658
|
function decodeOrphanAuditCursors(value) {
|
|
102263
102659
|
const empty = {
|
|
@@ -102269,7 +102665,7 @@ function decodeOrphanAuditCursors(value) {
|
|
|
102269
102665
|
if (value === null || typeof value !== "object") return empty;
|
|
102270
102666
|
const row = { ...value };
|
|
102271
102667
|
return {
|
|
102272
|
-
scopes:
|
|
102668
|
+
scopes: decodeScopeCursors(row["scopes"]),
|
|
102273
102669
|
faceVectorCursor: nonNegativeInt(row["faceVectorCursor"]) ?? 0,
|
|
102274
102670
|
clipIndexCursors: decodeCursorMap(row["clipIndexCursors"]),
|
|
102275
102671
|
updatedAtMs: nonNegativeInt(row["updatedAtMs"]) ?? 0
|
|
@@ -103842,7 +104238,9 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
103842
104238
|
};
|
|
103843
104239
|
const deviceIds = /* @__PURE__ */ new Set();
|
|
103844
104240
|
const graceFloor = deps.now() - limits.graceMs;
|
|
103845
|
-
let offset = Math.max(0, cursor);
|
|
104241
|
+
let offset = typeof cursor === "number" ? Math.max(0, cursor) : 0;
|
|
104242
|
+
let keysetCursor = typeof cursor === "string" ? cursor : null;
|
|
104243
|
+
let keyed = keysetCursor !== null;
|
|
103846
104244
|
let stoppedBy = "exhausted";
|
|
103847
104245
|
let collectionSize = null;
|
|
103848
104246
|
try {
|
|
@@ -103859,9 +104257,16 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
103859
104257
|
stoppedBy = "row-ceiling";
|
|
103860
104258
|
break;
|
|
103861
104259
|
}
|
|
103862
|
-
|
|
104260
|
+
if (deps.stop?.() === true) {
|
|
104261
|
+
stoppedBy = "stopped";
|
|
104262
|
+
break;
|
|
104263
|
+
}
|
|
104264
|
+
const keyedPage = deps.pageKeyset === void 0 ? null : await deps.pageKeyset(scope.collection, keysetCursor, limits.pageSize);
|
|
104265
|
+
keyed = keyedPage !== null;
|
|
104266
|
+
const rows = keyedPage !== null ? keyedPage.rows : await deps.page(scope.collection, scope.shard, offset, limits.pageSize);
|
|
103863
104267
|
if (rows.length === 0) {
|
|
103864
104268
|
offset = 0;
|
|
104269
|
+
keysetCursor = null;
|
|
103865
104270
|
break;
|
|
103866
104271
|
}
|
|
103867
104272
|
const refs = rows.map((row) => scope.resolve(row));
|
|
@@ -103948,9 +104353,17 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
103948
104353
|
if (deps.breathe) await deps.breathe();
|
|
103949
104354
|
if (ceilingHit) {
|
|
103950
104355
|
stoppedBy = "reclaim-ceiling";
|
|
103951
|
-
offset += rows.length - reclaimedInPage;
|
|
104356
|
+
if (!keyed) offset += rows.length - reclaimedInPage;
|
|
103952
104357
|
break;
|
|
103953
104358
|
}
|
|
104359
|
+
if (keyedPage !== null) {
|
|
104360
|
+
if (keyedPage.nextCursor === null) {
|
|
104361
|
+
keysetCursor = null;
|
|
104362
|
+
break;
|
|
104363
|
+
}
|
|
104364
|
+
keysetCursor = keyedPage.nextCursor;
|
|
104365
|
+
continue;
|
|
104366
|
+
}
|
|
103954
104367
|
const advanced = rows.length - reclaimedInPage;
|
|
103955
104368
|
if (advanced === 0 && rows.length < limits.pageSize) {
|
|
103956
104369
|
offset = 0;
|
|
@@ -103966,7 +104379,7 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
103966
104379
|
collectionSize,
|
|
103967
104380
|
coveragePercent: Math.round(tally.examined / collectionSize * 1e3) / 10,
|
|
103968
104381
|
stoppedBy,
|
|
103969
|
-
nextCursor: offset
|
|
104382
|
+
nextCursor: keyed ? keysetCursor ?? 0 : offset
|
|
103970
104383
|
} });
|
|
103971
104384
|
const freedNothing = scope.sizeField === null ? tally.reclaimed === 0 : tally.bytesReclaimed === 0;
|
|
103972
104385
|
if (limits.mode === "reclaim" && tally.orphans > 0 && freedNothing) deps.logger.warn("orphan audit: reclaim pass freed no bytes against a standing backlog", { meta: {
|
|
@@ -103993,7 +104406,7 @@ async function auditScope(scope, deps, limits, cursor) {
|
|
|
103993
104406
|
unresolvable: tally.unresolvable,
|
|
103994
104407
|
deviceIds: [...deviceIds],
|
|
103995
104408
|
stoppedBy,
|
|
103996
|
-
nextCursor: offset
|
|
104409
|
+
nextCursor: keyed ? keysetCursor ?? 0 : offset
|
|
103997
104410
|
};
|
|
103998
104411
|
}
|
|
103999
104412
|
/**
|
|
@@ -104085,7 +104498,8 @@ var ORPHAN_AUDIT_DEFAULTS = {
|
|
|
104085
104498
|
//#endregion
|
|
104086
104499
|
//#region src/pipeline-analytics/retention/orphan-reclaimers.ts
|
|
104087
104500
|
var OBJECT_EVENTS_COLLECTION = "pipeline-analytics:object-events";
|
|
104088
|
-
var
|
|
104501
|
+
var PLATES_COLLECTION = "pipeline-analytics:plates";
|
|
104502
|
+
var RAW_DELETE_COLLECTIONS = new Set([OBJECT_EVENTS_COLLECTION]);
|
|
104089
104503
|
/**
|
|
104090
104504
|
* The `deleteRow` port: ONE row by its primary key `id` (what every
|
|
104091
104505
|
* raw-delete collection above declares), answering whether a row went.
|
|
@@ -104159,6 +104573,13 @@ function buildOrphanReclaimer(ports) {
|
|
|
104159
104573
|
rows: await ports.deleteTrackAlias(key) ? 1 : 0,
|
|
104160
104574
|
bytes: null
|
|
104161
104575
|
};
|
|
104576
|
+
if (collection === PLATES_COLLECTION) {
|
|
104577
|
+
const assert = trackIdAssertOf(data);
|
|
104578
|
+
return {
|
|
104579
|
+
rows: await ports.deletePlate(key, assert === null ? void 0 : assert) ? 1 : 0,
|
|
104580
|
+
bytes: null
|
|
104581
|
+
};
|
|
104582
|
+
}
|
|
104162
104583
|
if (RAW_DELETE_COLLECTIONS.has(collection)) {
|
|
104163
104584
|
const assert = trackIdAssertOf(data);
|
|
104164
104585
|
if (collection === OBJECT_EVENTS_COLLECTION && assert === null) {
|
|
@@ -104180,6 +104601,87 @@ function buildOrphanReclaimer(ports) {
|
|
|
104180
104601
|
};
|
|
104181
104602
|
}
|
|
104182
104603
|
//#endregion
|
|
104604
|
+
//#region src/pipeline-analytics/retention/orphan-audit-ledgers.ts
|
|
104605
|
+
/** Audited collection → its records-kind ledger. Empty without records. */
|
|
104606
|
+
function orphanAuditLedgers(wiring) {
|
|
104607
|
+
if (wiring.records === void 0) return /* @__PURE__ */ new Map();
|
|
104608
|
+
return new Map([
|
|
104609
|
+
[FACES_COLLECTION, wireFaceLedger(wiring)],
|
|
104610
|
+
[PLATES_COLLECTION$1, wirePlateLedger(wiring)],
|
|
104611
|
+
[TRACK_ALIASES_COLLECTION, wireTrackAliasLedger(wiring)]
|
|
104612
|
+
]);
|
|
104613
|
+
}
|
|
104614
|
+
/** {@link OrphanAuditDeps.pageKeyset} over those ledgers; `null` for any other collection. */
|
|
104615
|
+
function keysetPager(ledgers) {
|
|
104616
|
+
return async (collection, cursor, limit) => {
|
|
104617
|
+
const ledger = ledgers.get(collection);
|
|
104618
|
+
if (ledger === void 0) return null;
|
|
104619
|
+
const page = await ledger.pageById(cursor, limit);
|
|
104620
|
+
return {
|
|
104621
|
+
rows: page.rows.map((row) => ({
|
|
104622
|
+
key: row.id,
|
|
104623
|
+
data: row.data
|
|
104624
|
+
})),
|
|
104625
|
+
nextCursor: page.nextCursor
|
|
104626
|
+
};
|
|
104627
|
+
};
|
|
104628
|
+
}
|
|
104629
|
+
/** The kind's count for a ledger-backed collection, `undefined` for any other. */
|
|
104630
|
+
async function ledgerSize(ledgers, collection) {
|
|
104631
|
+
const ledger = ledgers.get(collection);
|
|
104632
|
+
return ledger === void 0 ? void 0 : ledger.count();
|
|
104633
|
+
}
|
|
104634
|
+
/**
|
|
104635
|
+
* The plate reclaim through the plate ledger — records on a hub, the legacy
|
|
104636
|
+
* collection only without records. With `assert`, a row whose `trackId` is not
|
|
104637
|
+
* the dead track's is KEPT (logged), never deleted.
|
|
104638
|
+
*/
|
|
104639
|
+
function plateDeleter(plates, logger) {
|
|
104640
|
+
return async (plateId, assert) => {
|
|
104641
|
+
const row = await plates.get(plateId);
|
|
104642
|
+
if (row === null) return false;
|
|
104643
|
+
if (assert !== void 0 && row.data["trackId"] !== assert.trackId) {
|
|
104644
|
+
const deviceId = row.data["deviceId"];
|
|
104645
|
+
logger.warn("orphan audit: plate row now names another track — kept", {
|
|
104646
|
+
...typeof deviceId === "number" ? { tags: { deviceId } } : {},
|
|
104647
|
+
meta: {
|
|
104648
|
+
plateId,
|
|
104649
|
+
expectedTrackId: assert.trackId
|
|
104650
|
+
}
|
|
104651
|
+
});
|
|
104652
|
+
return false;
|
|
104653
|
+
}
|
|
104654
|
+
await plates.deleteKey(plateId);
|
|
104655
|
+
return true;
|
|
104656
|
+
};
|
|
104657
|
+
}
|
|
104658
|
+
function auditCollectionPorts(store, ledgers) {
|
|
104659
|
+
return {
|
|
104660
|
+
pageKeyset: keysetPager(ledgers),
|
|
104661
|
+
pageByOffset: async (collection, offset, limit) => {
|
|
104662
|
+
return (await store.query.query({
|
|
104663
|
+
collection,
|
|
104664
|
+
filter: {
|
|
104665
|
+
orderBy: {
|
|
104666
|
+
field: "id",
|
|
104667
|
+
direction: "asc"
|
|
104668
|
+
},
|
|
104669
|
+
limit,
|
|
104670
|
+
offset
|
|
104671
|
+
}
|
|
104672
|
+
})).map((row) => ({
|
|
104673
|
+
key: row.id,
|
|
104674
|
+
data: row.data
|
|
104675
|
+
}));
|
|
104676
|
+
},
|
|
104677
|
+
size: async (collection) => {
|
|
104678
|
+
const counted = await ledgerSize(ledgers, collection);
|
|
104679
|
+
if (counted !== void 0) return counted;
|
|
104680
|
+
return store.count.query({ collection });
|
|
104681
|
+
}
|
|
104682
|
+
};
|
|
104683
|
+
}
|
|
104684
|
+
//#endregion
|
|
104183
104685
|
//#region src/pipeline-analytics/retention/retired-device-settings.ts
|
|
104184
104686
|
/**
|
|
104185
104687
|
* Every retired knob, with the sentence that explains it. The list is the
|
|
@@ -114937,6 +115439,7 @@ var customActions = {
|
|
|
114937
115439
|
...orphanAuditActions,
|
|
114938
115440
|
...mediaReclaimActions,
|
|
114939
115441
|
...locationDiscardActions,
|
|
115442
|
+
...mediaForgetActions,
|
|
114940
115443
|
...photoEnrollActions,
|
|
114941
115444
|
...faceRescoreActions,
|
|
114942
115445
|
...stillnessCalibrationActions
|
|
@@ -116727,6 +117230,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
116727
117230
|
orphanAuditState = { ...IDLE_ORPHAN_AUDIT };
|
|
116728
117231
|
/** Frozen-location leftover wipe (events/ on a drained HDD). Detached. */
|
|
116729
117232
|
discardJob = null;
|
|
117233
|
+
/** The narrow media-row forget's wiring (`storage.forgetMissingMedia`). */
|
|
117234
|
+
mediaForgetDeps = null;
|
|
116730
117235
|
/**
|
|
116731
117236
|
* The CLIP indexes' walks (D649), one resume cursor per index, kept in their
|
|
116732
117237
|
* OWN field rather than in {@link orphanAuditCursors}: `runOrphanAudit`
|
|
@@ -116935,10 +117440,17 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
116935
117440
|
const isHub = ownNodeId === "hub";
|
|
116936
117441
|
const ncHandlers = isHub ? this.buildNcActionHandlers(api) : void 0;
|
|
116937
117442
|
if (isHub) this.reconcileTapePinsInBackground();
|
|
117443
|
+
this.mediaForgetDeps = {
|
|
117444
|
+
listLocations: async () => api.storage.listLocations.query({}),
|
|
117445
|
+
localNodeId: ownNodeId,
|
|
117446
|
+
store: api.settingsStore,
|
|
117447
|
+
logger: logger.child("MediaForget")
|
|
117448
|
+
};
|
|
116938
117449
|
this.discardJob = createDiscardJob({
|
|
116939
117450
|
listLocations: async () => api.storage.listLocations.query({}),
|
|
116940
117451
|
localNodeId: ownNodeId,
|
|
116941
117452
|
store: api.settingsStore,
|
|
117453
|
+
retrainFrameLedger: wireRetrainFrameLedger(this.eventLedgerWiring(api.settingsStore)),
|
|
116942
117454
|
deleteTrackEvents: async (trackId) => {
|
|
116943
117455
|
await this.eventStore?.deleteByTrack(trackId);
|
|
116944
117456
|
},
|
|
@@ -116959,6 +117471,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
116959
117471
|
...orphanAuditActions,
|
|
116960
117472
|
...mediaReclaimActions,
|
|
116961
117473
|
...locationDiscardActions,
|
|
117474
|
+
...mediaForgetActions,
|
|
116962
117475
|
...photoEnrollActions,
|
|
116963
117476
|
...faceRescoreActions,
|
|
116964
117477
|
...stillnessCalibrationActions
|
|
@@ -117554,6 +118067,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
117554
118067
|
});
|
|
117555
118068
|
this.mediaRelocate = new MediaRelocateEngine({
|
|
117556
118069
|
store: api.settingsStore,
|
|
118070
|
+
retrainFrameLedger: wireRetrainFrameLedger(eventLedgerWiring),
|
|
117557
118071
|
storage,
|
|
117558
118072
|
logger: logger.child("MediaRelocate"),
|
|
117559
118073
|
now: () => Date.now(),
|
|
@@ -125296,36 +125810,29 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
125296
125810
|
const trackStore = this.trackStore;
|
|
125297
125811
|
const eventStore = this.eventStore;
|
|
125298
125812
|
const mediaStore = this.mediaStore;
|
|
125299
|
-
|
|
125813
|
+
const settingsStore = this.settingsStore;
|
|
125814
|
+
if (!trackStore || !eventStore || !mediaStore || !settingsStore) {
|
|
125300
125815
|
if (!alreadyHeld) this.orphanAuditRunning = false;
|
|
125301
125816
|
return null;
|
|
125302
125817
|
}
|
|
125303
125818
|
try {
|
|
125304
125819
|
const limits = await this.orphanAuditLimits(overrides);
|
|
125820
|
+
const auditLedgers = orphanAuditLedgers(this.eventLedgerWiring(settingsStore));
|
|
125821
|
+
const plateLedger = wirePlateLedger(this.eventLedgerWiring(settingsStore));
|
|
125822
|
+
const collectionPorts = auditCollectionPorts(settingsStore, auditLedgers);
|
|
125305
125823
|
const deps = {
|
|
125824
|
+
pageKeyset: collectionPorts.pageKeyset,
|
|
125825
|
+
stop: () => this.shuttingDown,
|
|
125306
125826
|
page: async (collection, shard, offset, limit) => {
|
|
125307
125827
|
if (collection === MEDIA_COLLECTION) return (await mediaStore.pageForAudit(shard?.value ?? "", offset, limit)).map((row) => ({
|
|
125308
125828
|
key: String(row.mediaId),
|
|
125309
125829
|
data: row.data
|
|
125310
125830
|
}));
|
|
125311
|
-
return (
|
|
125312
|
-
collection,
|
|
125313
|
-
filter: {
|
|
125314
|
-
orderBy: {
|
|
125315
|
-
field: "id",
|
|
125316
|
-
direction: "asc"
|
|
125317
|
-
},
|
|
125318
|
-
limit,
|
|
125319
|
-
offset
|
|
125320
|
-
}
|
|
125321
|
-
}) ?? []).map((row) => ({
|
|
125322
|
-
key: row.id,
|
|
125323
|
-
data: row.data
|
|
125324
|
-
}));
|
|
125831
|
+
return collectionPorts.pageByOffset(collection, offset, limit);
|
|
125325
125832
|
},
|
|
125326
125833
|
size: async (collection, shard) => {
|
|
125327
125834
|
if (collection === MEDIA_COLLECTION) return mediaStore.countForAudit(shard?.value ?? "");
|
|
125328
|
-
return
|
|
125835
|
+
return collectionPorts.size(collection);
|
|
125329
125836
|
},
|
|
125330
125837
|
liveTrackIds: (ids) => this.liveTrackIdsThroughAliases(ids, trackStore),
|
|
125331
125838
|
liveEventIds: (table, ids) => eventStore.existingEventIdsIn(table, ids),
|
|
@@ -125348,6 +125855,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
125348
125855
|
if (aliasStore === null) throw new Error("track alias store not bound");
|
|
125349
125856
|
return await aliasStore.deleteAlias(aliasId);
|
|
125350
125857
|
},
|
|
125858
|
+
deletePlate: plateDeleter(plateLedger, this.ctx.logger),
|
|
125351
125859
|
deleteRow: settingsRowDeleterOf(() => this.settingsStore),
|
|
125352
125860
|
logger: this.ctx.logger
|
|
125353
125861
|
});
|
|
@@ -126769,7 +127277,12 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
126769
127277
|
jobId: null
|
|
126770
127278
|
},
|
|
126771
127279
|
"storage.discardLocationStatus": async () => this.discardJob?.status() ?? IDLE_DISCARD,
|
|
126772
|
-
"storage.discardLocationCancel": async () => this.discardJob?.cancel() ?? { requested: false }
|
|
127280
|
+
"storage.discardLocationCancel": async () => this.discardJob?.cancel() ?? { requested: false },
|
|
127281
|
+
"storage.forgetMissingMedia": async (input) => {
|
|
127282
|
+
const deps = this.mediaForgetDeps;
|
|
127283
|
+
if (deps === null) throw new Error("storage.forgetMissingMedia: not initialised");
|
|
127284
|
+
return forgetMissingMediaRows(deps, input);
|
|
127285
|
+
}
|
|
126773
127286
|
};
|
|
126774
127287
|
}
|
|
126775
127288
|
/**
|