@camstack/addon-pipeline 1.2.100 → 1.2.102

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.
Files changed (34) hide show
  1. package/dist/{addon-utils-eUS6n_Zj.js → addon-utils-CLc6yHCN.js} +1 -1
  2. package/dist/audio-analyzer/index.js +3 -3
  3. package/dist/audio-analyzer/index.mjs +2 -2
  4. package/dist/detection-pipeline/index.js +163 -42
  5. package/dist/detection-pipeline/index.mjs +161 -40
  6. package/dist/{dist-dm3t4BOt.js → dist-C11WuNUP.js} +87 -51
  7. package/dist/{dist-gXdWP96z.mjs → dist-Ccmt3fGJ.mjs} +82 -52
  8. package/dist/{event-loop-stall-monitor-Lki2_mYY.js → event-loop-stall-monitor-Cq_NeC4o.js} +175 -132
  9. package/dist/{event-loop-stall-monitor-TnQ21mu0.mjs → event-loop-stall-monitor-OJrOMeuu.mjs} +170 -133
  10. package/dist/{lazy-sharp-oYppDIZR.js → lazy-sharp-RxUs6on_.js} +1 -1
  11. package/dist/motion-wasm/index.js +2 -2
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/pipeline-runner/index.js +5 -4
  14. package/dist/pipeline-runner/index.mjs +4 -3
  15. package/dist/{process-memory-CKWh609B.mjs → process-memory-D0zDmXLI.mjs} +1 -1
  16. package/dist/{process-memory-vkFVOdkV.js → process-memory-DOjQ3MgC.js} +1 -1
  17. package/dist/recorder/index.js +333 -125
  18. package/dist/recorder/index.mjs +332 -124
  19. package/dist/retire-root-keys-DMolfhsP.mjs +308 -0
  20. package/dist/retire-root-keys-KE6D6Xh_.js +319 -0
  21. package/dist/session-decode/decode-worker-child.js +2 -2
  22. package/dist/session-decode/decode-worker-child.mjs +1 -1
  23. package/dist/stream-broker/_stub.js +1 -1
  24. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B7ERJocD.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BdgcF1lL.mjs} +2 -2
  25. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-zT-cYUdF.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DY31bUCj.mjs} +1 -1
  26. package/dist/stream-broker/{hostInit-Cm2rl8-e.mjs → hostInit-Da9wVA2r.mjs} +2 -2
  27. package/dist/stream-broker/index.js +285 -41
  28. package/dist/stream-broker/index.mjs +285 -41
  29. package/dist/stream-broker/remoteEntry.js +1 -1
  30. package/dist/{worker-protocol-BrlrMxXu.js → worker-protocol-BePduZVV.js} +1 -1
  31. package/dist/{worker-protocol-DJWdPLKF.mjs → worker-protocol-DDpliBIW.mjs} +1 -1
  32. package/package.json +1 -1
  33. package/python/postprocessors/ctc.py +56 -3
  34. package/python/postprocessors/test_ctc.py +37 -0
@@ -0,0 +1,308 @@
1
+ var ROW_MAP_VALUE_COLUMN = "value";
2
+ var RowMapStore = class RowMapStore {
3
+ spec;
4
+ store;
5
+ logger;
6
+ constructor(deps) {
7
+ this.spec = deps.spec;
8
+ this.store = deps.store;
9
+ this.logger = deps.logger;
10
+ }
11
+ /** The collection this store owns — for the caller's own log lines. */
12
+ get collection() {
13
+ return this.spec.collection;
14
+ }
15
+ /**
16
+ * Register the collection. MUST run at boot, before any read or write: the
17
+ * SQLite backend answers 412 for an undeclared collection.
18
+ */
19
+ static declare(store, spec) {
20
+ return store.declareCollection.mutate({
21
+ collection: spec.collection,
22
+ columns: [
23
+ {
24
+ name: "id",
25
+ type: "TEXT",
26
+ primaryKey: true,
27
+ notNull: true
28
+ },
29
+ ...spec.columns,
30
+ {
31
+ name: ROW_MAP_VALUE_COLUMN,
32
+ type: "JSON",
33
+ notNull: true
34
+ }
35
+ ],
36
+ ...spec.indexes !== void 0 ? { indexes: [...spec.indexes] } : {}
37
+ });
38
+ }
39
+ declare() {
40
+ return RowMapStore.declare(this.store, this.spec);
41
+ }
42
+ /** One entry, by key. `null` when absent OR unparseable (which is said aloud). */
43
+ async read(key) {
44
+ const raw = await this.store.get.query({
45
+ collection: this.spec.collection,
46
+ key
47
+ });
48
+ if (!isRecord$1(raw)) return null;
49
+ const row = raw;
50
+ const parsed = this.spec.schema.safeParse(row[ROW_MAP_VALUE_COLUMN]);
51
+ if (!parsed.success) {
52
+ this.logger.warn("row-map row skipped as unparseable", {
53
+ ...this.deviceTags(row),
54
+ meta: {
55
+ collection: this.spec.collection,
56
+ key,
57
+ error: parsed.error.message
58
+ }
59
+ });
60
+ return null;
61
+ }
62
+ return parsed.data;
63
+ }
64
+ /**
65
+ * Every entry, keyed. Unparseable rows are skipped — one bad entry costs that
66
+ * entry, never the map.
67
+ */
68
+ async readAll() {
69
+ const records = await this.store.query.query({
70
+ collection: this.spec.collection,
71
+ filter: { limit: this.spec.loadLimit }
72
+ });
73
+ const out = /* @__PURE__ */ new Map();
74
+ let skipped = 0;
75
+ for (const record of records) {
76
+ const parsed = this.spec.schema.safeParse(record.data[ROW_MAP_VALUE_COLUMN]);
77
+ if (!parsed.success) {
78
+ skipped += 1;
79
+ continue;
80
+ }
81
+ out.set(record.id, parsed.data);
82
+ }
83
+ if (skipped > 0) this.logger.warn("row-map rows skipped as unparseable", { meta: {
84
+ collection: this.spec.collection,
85
+ skipped,
86
+ kept: out.size
87
+ } });
88
+ if (records.length >= this.spec.loadLimit) this.logger.warn("row-map read hit its own bound — the map may be truncated", { meta: {
89
+ collection: this.spec.collection,
90
+ loadLimit: this.spec.loadLimit
91
+ } });
92
+ return out;
93
+ }
94
+ /** Insert or replace ONE entry. Validates at the boundary. */
95
+ async write(key, value) {
96
+ const validated = this.spec.schema.parse(value);
97
+ const columns = this.spec.project(key, validated);
98
+ await this.store.set.mutate({
99
+ collection: this.spec.collection,
100
+ key,
101
+ value: {
102
+ ...columns,
103
+ [ROW_MAP_VALUE_COLUMN]: validated
104
+ }
105
+ });
106
+ }
107
+ /** Drop ONE entry. Idempotent. */
108
+ async remove(key) {
109
+ await this.store.delete.mutate({
110
+ collection: this.spec.collection,
111
+ key
112
+ });
113
+ }
114
+ /**
115
+ * Make the collection hold exactly `next` — the write path for an owner whose
116
+ * in-memory map IS the authority (a persister handed the complete set).
117
+ *
118
+ * Returns how many rows were DELETED, and says so out loud when any were: a
119
+ * replace that silently drops rows is indistinguishable from one that never
120
+ * had them. Only an owner holding an authoritative set may call this — a
121
+ * prune driven by a fallible read is work destroyed (D130, D49).
122
+ */
123
+ async replaceAll(next) {
124
+ const held = await this.store.query.query({
125
+ collection: this.spec.collection,
126
+ filter: { limit: this.spec.loadLimit }
127
+ });
128
+ for (const [key, value] of next) await this.write(key, value);
129
+ const doomed = held.filter((record) => !next.has(record.id));
130
+ for (const record of doomed) await this.remove(record.id);
131
+ if (doomed.length > 0) this.logger.warn("row-map replaceAll dropped rows", { meta: {
132
+ collection: this.spec.collection,
133
+ dropped: doomed.length,
134
+ keys: doomed.slice(0, 20).map((r) => r.id),
135
+ remaining: next.size
136
+ } });
137
+ return doomed.length;
138
+ }
139
+ /**
140
+ * The entries matching a projected-column predicate, in the order asked for.
141
+ *
142
+ * This is what projecting columns BUYS: "every export for camera 617, newest
143
+ * first, capped at 50" is an indexed `WHERE` + `ORDER BY` + `LIMIT`, not a
144
+ * parse of the whole map followed by a filter in JS.
145
+ *
146
+ * A `limit` is REQUIRED — `settings-store.query` silently caps an unbounded
147
+ * read at 2 000 rows and answers as if that were everything.
148
+ */
149
+ async readWhere(filter) {
150
+ const records = await this.store.query.query({
151
+ collection: this.spec.collection,
152
+ filter: {
153
+ ...filter.where !== void 0 ? { where: filter.where } : {},
154
+ ...filter.whereIn !== void 0 ? { whereIn: filter.whereIn } : {},
155
+ ...filter.whereBetween !== void 0 ? { whereBetween: filter.whereBetween } : {},
156
+ ...filter.orderBy !== void 0 ? { orderBy: filter.orderBy } : {},
157
+ ...filter.offset !== void 0 ? { offset: filter.offset } : {},
158
+ limit: filter.limit ?? this.spec.loadLimit
159
+ }
160
+ });
161
+ const out = [];
162
+ let skipped = 0;
163
+ for (const record of records) {
164
+ const parsed = this.spec.schema.safeParse(record.data[ROW_MAP_VALUE_COLUMN]);
165
+ if (!parsed.success) {
166
+ skipped += 1;
167
+ continue;
168
+ }
169
+ out.push({
170
+ key: record.id,
171
+ value: parsed.data
172
+ });
173
+ }
174
+ if (skipped > 0) this.logger.warn("row-map rows skipped as unparseable", { meta: {
175
+ collection: this.spec.collection,
176
+ skipped,
177
+ kept: out.length
178
+ } });
179
+ return out;
180
+ }
181
+ /** How many rows match, without materialising one. */
182
+ async count(filter) {
183
+ return this.store.count.query({
184
+ collection: this.spec.collection,
185
+ ...filter !== void 0 ? { filter: {
186
+ ...filter.where !== void 0 ? { where: filter.where } : {},
187
+ ...filter.whereIn !== void 0 ? { whereIn: filter.whereIn } : {},
188
+ ...filter.whereBetween !== void 0 ? { whereBetween: filter.whereBetween } : {}
189
+ } } : {}
190
+ });
191
+ }
192
+ /**
193
+ * Delete every row matching a predicate, in ONE statement. Returns how many
194
+ * went, and says so — a retention trim that silently drops rows is
195
+ * indistinguishable from one that had nothing to do.
196
+ *
197
+ * The predicate is REQUIRED and the engine REFUSES one that compiles to
198
+ * nothing: deleting a whole collection is a legitimate intent, but it must be
199
+ * asked for by name rather than reached through an empty object.
200
+ */
201
+ async deleteWhere(filter) {
202
+ const { deleted } = await this.store.deleteWhere.mutate({
203
+ collection: this.spec.collection,
204
+ filter
205
+ });
206
+ if (deleted > 0) this.logger.warn("row-map deleteWhere dropped rows", { meta: {
207
+ collection: this.spec.collection,
208
+ deleted,
209
+ filter
210
+ } });
211
+ return deleted;
212
+ }
213
+ /** `tags: { deviceId }` off the PROJECTED column, when the spec names one. */
214
+ deviceTags(row) {
215
+ const column = this.spec.deviceIdColumn;
216
+ if (column === void 0) return {};
217
+ const raw = row[column];
218
+ return typeof raw === "number" && Number.isInteger(raw) ? { tags: { deviceId: raw } } : {};
219
+ }
220
+ };
221
+ /** A store payload that is a plain object, or `null`. A type guard, not a cast. */
222
+ function isRecord$1(value) {
223
+ return value !== null && typeof value === "object" && !Array.isArray(value);
224
+ }
225
+ //#endregion
226
+ //#region src/durable/retire-root-keys.ts
227
+ /** The canonical row a forked addon's settings live in. */
228
+ var ADDON_SETTINGS_COLLECTION = "addon-settings";
229
+ var ADDON_SETTINGS_ROW = "root";
230
+ /**
231
+ * Drop every retired key whose successor is populated. Returns the keys that
232
+ * actually went — empty when there was nothing to do.
233
+ */
234
+ async function retireRootKeys(deps) {
235
+ let row;
236
+ try {
237
+ const raw = await deps.store.get.query({
238
+ namespace: deps.addonId,
239
+ collection: ADDON_SETTINGS_COLLECTION,
240
+ key: ADDON_SETTINGS_ROW
241
+ });
242
+ if (!isRecord(raw)) return [];
243
+ row = raw;
244
+ } catch (err) {
245
+ deps.logger.warn("retired root keys — read failed, the settings row is untouched", { meta: {
246
+ addonId: deps.addonId,
247
+ error: errText(err)
248
+ } });
249
+ return [];
250
+ }
251
+ const present = deps.specs.filter((spec) => spec.key in row);
252
+ if (present.length === 0) return [];
253
+ /** Evidence counts, memoised — several keys may share one successor. */
254
+ const evidence = /* @__PURE__ */ new Map();
255
+ const doomed = [];
256
+ for (const spec of present) {
257
+ let count = evidence.get(spec.successor);
258
+ if (count === void 0) {
259
+ try {
260
+ count = await deps.store.count.query({ collection: spec.successor });
261
+ } catch (err) {
262
+ deps.logger.warn("retired root key kept — its successor is unreadable", { meta: {
263
+ addonId: deps.addonId,
264
+ key: spec.key,
265
+ successor: spec.successor,
266
+ error: errText(err)
267
+ } });
268
+ count = 0;
269
+ }
270
+ evidence.set(spec.successor, count);
271
+ }
272
+ if (count === 0) {
273
+ deps.logger.warn("retired root key kept — its successor is EMPTY, migration has not run", { meta: {
274
+ addonId: deps.addonId,
275
+ key: spec.key,
276
+ successor: spec.successor
277
+ } });
278
+ continue;
279
+ }
280
+ doomed.push(spec);
281
+ }
282
+ if (doomed.length === 0) return [];
283
+ const dropped = new Set(doomed.map((spec) => spec.key));
284
+ const next = Object.fromEntries(Object.entries(row).filter(([key]) => !dropped.has(key)));
285
+ await deps.store.set.mutate({
286
+ namespace: deps.addonId,
287
+ collection: ADDON_SETTINGS_COLLECTION,
288
+ key: ADDON_SETTINGS_ROW,
289
+ value: next
290
+ });
291
+ const keys = doomed.map((spec) => spec.key);
292
+ deps.logger.info(`purged ${keys.length} retired settings key(s) from the root blob`, { meta: {
293
+ addonId: deps.addonId,
294
+ keys,
295
+ keptKeys: Object.keys(next),
296
+ reasons: doomed.map((spec) => `${spec.key}: ${spec.reason}`)
297
+ } });
298
+ return keys;
299
+ }
300
+ /** A store payload that is a plain object. A type guard, not a cast. */
301
+ function isRecord(value) {
302
+ return value !== null && typeof value === "object" && !Array.isArray(value);
303
+ }
304
+ function errText(err) {
305
+ return err instanceof Error ? err.message : String(err);
306
+ }
307
+ //#endregion
308
+ export { RowMapStore as n, retireRootKeys as t };
@@ -0,0 +1,319 @@
1
+ var ROW_MAP_VALUE_COLUMN = "value";
2
+ var RowMapStore = class RowMapStore {
3
+ spec;
4
+ store;
5
+ logger;
6
+ constructor(deps) {
7
+ this.spec = deps.spec;
8
+ this.store = deps.store;
9
+ this.logger = deps.logger;
10
+ }
11
+ /** The collection this store owns — for the caller's own log lines. */
12
+ get collection() {
13
+ return this.spec.collection;
14
+ }
15
+ /**
16
+ * Register the collection. MUST run at boot, before any read or write: the
17
+ * SQLite backend answers 412 for an undeclared collection.
18
+ */
19
+ static declare(store, spec) {
20
+ return store.declareCollection.mutate({
21
+ collection: spec.collection,
22
+ columns: [
23
+ {
24
+ name: "id",
25
+ type: "TEXT",
26
+ primaryKey: true,
27
+ notNull: true
28
+ },
29
+ ...spec.columns,
30
+ {
31
+ name: ROW_MAP_VALUE_COLUMN,
32
+ type: "JSON",
33
+ notNull: true
34
+ }
35
+ ],
36
+ ...spec.indexes !== void 0 ? { indexes: [...spec.indexes] } : {}
37
+ });
38
+ }
39
+ declare() {
40
+ return RowMapStore.declare(this.store, this.spec);
41
+ }
42
+ /** One entry, by key. `null` when absent OR unparseable (which is said aloud). */
43
+ async read(key) {
44
+ const raw = await this.store.get.query({
45
+ collection: this.spec.collection,
46
+ key
47
+ });
48
+ if (!isRecord$1(raw)) return null;
49
+ const row = raw;
50
+ const parsed = this.spec.schema.safeParse(row[ROW_MAP_VALUE_COLUMN]);
51
+ if (!parsed.success) {
52
+ this.logger.warn("row-map row skipped as unparseable", {
53
+ ...this.deviceTags(row),
54
+ meta: {
55
+ collection: this.spec.collection,
56
+ key,
57
+ error: parsed.error.message
58
+ }
59
+ });
60
+ return null;
61
+ }
62
+ return parsed.data;
63
+ }
64
+ /**
65
+ * Every entry, keyed. Unparseable rows are skipped — one bad entry costs that
66
+ * entry, never the map.
67
+ */
68
+ async readAll() {
69
+ const records = await this.store.query.query({
70
+ collection: this.spec.collection,
71
+ filter: { limit: this.spec.loadLimit }
72
+ });
73
+ const out = /* @__PURE__ */ new Map();
74
+ let skipped = 0;
75
+ for (const record of records) {
76
+ const parsed = this.spec.schema.safeParse(record.data[ROW_MAP_VALUE_COLUMN]);
77
+ if (!parsed.success) {
78
+ skipped += 1;
79
+ continue;
80
+ }
81
+ out.set(record.id, parsed.data);
82
+ }
83
+ if (skipped > 0) this.logger.warn("row-map rows skipped as unparseable", { meta: {
84
+ collection: this.spec.collection,
85
+ skipped,
86
+ kept: out.size
87
+ } });
88
+ if (records.length >= this.spec.loadLimit) this.logger.warn("row-map read hit its own bound — the map may be truncated", { meta: {
89
+ collection: this.spec.collection,
90
+ loadLimit: this.spec.loadLimit
91
+ } });
92
+ return out;
93
+ }
94
+ /** Insert or replace ONE entry. Validates at the boundary. */
95
+ async write(key, value) {
96
+ const validated = this.spec.schema.parse(value);
97
+ const columns = this.spec.project(key, validated);
98
+ await this.store.set.mutate({
99
+ collection: this.spec.collection,
100
+ key,
101
+ value: {
102
+ ...columns,
103
+ [ROW_MAP_VALUE_COLUMN]: validated
104
+ }
105
+ });
106
+ }
107
+ /** Drop ONE entry. Idempotent. */
108
+ async remove(key) {
109
+ await this.store.delete.mutate({
110
+ collection: this.spec.collection,
111
+ key
112
+ });
113
+ }
114
+ /**
115
+ * Make the collection hold exactly `next` — the write path for an owner whose
116
+ * in-memory map IS the authority (a persister handed the complete set).
117
+ *
118
+ * Returns how many rows were DELETED, and says so out loud when any were: a
119
+ * replace that silently drops rows is indistinguishable from one that never
120
+ * had them. Only an owner holding an authoritative set may call this — a
121
+ * prune driven by a fallible read is work destroyed (D130, D49).
122
+ */
123
+ async replaceAll(next) {
124
+ const held = await this.store.query.query({
125
+ collection: this.spec.collection,
126
+ filter: { limit: this.spec.loadLimit }
127
+ });
128
+ for (const [key, value] of next) await this.write(key, value);
129
+ const doomed = held.filter((record) => !next.has(record.id));
130
+ for (const record of doomed) await this.remove(record.id);
131
+ if (doomed.length > 0) this.logger.warn("row-map replaceAll dropped rows", { meta: {
132
+ collection: this.spec.collection,
133
+ dropped: doomed.length,
134
+ keys: doomed.slice(0, 20).map((r) => r.id),
135
+ remaining: next.size
136
+ } });
137
+ return doomed.length;
138
+ }
139
+ /**
140
+ * The entries matching a projected-column predicate, in the order asked for.
141
+ *
142
+ * This is what projecting columns BUYS: "every export for camera 617, newest
143
+ * first, capped at 50" is an indexed `WHERE` + `ORDER BY` + `LIMIT`, not a
144
+ * parse of the whole map followed by a filter in JS.
145
+ *
146
+ * A `limit` is REQUIRED — `settings-store.query` silently caps an unbounded
147
+ * read at 2 000 rows and answers as if that were everything.
148
+ */
149
+ async readWhere(filter) {
150
+ const records = await this.store.query.query({
151
+ collection: this.spec.collection,
152
+ filter: {
153
+ ...filter.where !== void 0 ? { where: filter.where } : {},
154
+ ...filter.whereIn !== void 0 ? { whereIn: filter.whereIn } : {},
155
+ ...filter.whereBetween !== void 0 ? { whereBetween: filter.whereBetween } : {},
156
+ ...filter.orderBy !== void 0 ? { orderBy: filter.orderBy } : {},
157
+ ...filter.offset !== void 0 ? { offset: filter.offset } : {},
158
+ limit: filter.limit ?? this.spec.loadLimit
159
+ }
160
+ });
161
+ const out = [];
162
+ let skipped = 0;
163
+ for (const record of records) {
164
+ const parsed = this.spec.schema.safeParse(record.data[ROW_MAP_VALUE_COLUMN]);
165
+ if (!parsed.success) {
166
+ skipped += 1;
167
+ continue;
168
+ }
169
+ out.push({
170
+ key: record.id,
171
+ value: parsed.data
172
+ });
173
+ }
174
+ if (skipped > 0) this.logger.warn("row-map rows skipped as unparseable", { meta: {
175
+ collection: this.spec.collection,
176
+ skipped,
177
+ kept: out.length
178
+ } });
179
+ return out;
180
+ }
181
+ /** How many rows match, without materialising one. */
182
+ async count(filter) {
183
+ return this.store.count.query({
184
+ collection: this.spec.collection,
185
+ ...filter !== void 0 ? { filter: {
186
+ ...filter.where !== void 0 ? { where: filter.where } : {},
187
+ ...filter.whereIn !== void 0 ? { whereIn: filter.whereIn } : {},
188
+ ...filter.whereBetween !== void 0 ? { whereBetween: filter.whereBetween } : {}
189
+ } } : {}
190
+ });
191
+ }
192
+ /**
193
+ * Delete every row matching a predicate, in ONE statement. Returns how many
194
+ * went, and says so — a retention trim that silently drops rows is
195
+ * indistinguishable from one that had nothing to do.
196
+ *
197
+ * The predicate is REQUIRED and the engine REFUSES one that compiles to
198
+ * nothing: deleting a whole collection is a legitimate intent, but it must be
199
+ * asked for by name rather than reached through an empty object.
200
+ */
201
+ async deleteWhere(filter) {
202
+ const { deleted } = await this.store.deleteWhere.mutate({
203
+ collection: this.spec.collection,
204
+ filter
205
+ });
206
+ if (deleted > 0) this.logger.warn("row-map deleteWhere dropped rows", { meta: {
207
+ collection: this.spec.collection,
208
+ deleted,
209
+ filter
210
+ } });
211
+ return deleted;
212
+ }
213
+ /** `tags: { deviceId }` off the PROJECTED column, when the spec names one. */
214
+ deviceTags(row) {
215
+ const column = this.spec.deviceIdColumn;
216
+ if (column === void 0) return {};
217
+ const raw = row[column];
218
+ return typeof raw === "number" && Number.isInteger(raw) ? { tags: { deviceId: raw } } : {};
219
+ }
220
+ };
221
+ /** A store payload that is a plain object, or `null`. A type guard, not a cast. */
222
+ function isRecord$1(value) {
223
+ return value !== null && typeof value === "object" && !Array.isArray(value);
224
+ }
225
+ //#endregion
226
+ //#region src/durable/retire-root-keys.ts
227
+ /** The canonical row a forked addon's settings live in. */
228
+ var ADDON_SETTINGS_COLLECTION = "addon-settings";
229
+ var ADDON_SETTINGS_ROW = "root";
230
+ /**
231
+ * Drop every retired key whose successor is populated. Returns the keys that
232
+ * actually went — empty when there was nothing to do.
233
+ */
234
+ async function retireRootKeys(deps) {
235
+ let row;
236
+ try {
237
+ const raw = await deps.store.get.query({
238
+ namespace: deps.addonId,
239
+ collection: ADDON_SETTINGS_COLLECTION,
240
+ key: ADDON_SETTINGS_ROW
241
+ });
242
+ if (!isRecord(raw)) return [];
243
+ row = raw;
244
+ } catch (err) {
245
+ deps.logger.warn("retired root keys — read failed, the settings row is untouched", { meta: {
246
+ addonId: deps.addonId,
247
+ error: errText(err)
248
+ } });
249
+ return [];
250
+ }
251
+ const present = deps.specs.filter((spec) => spec.key in row);
252
+ if (present.length === 0) return [];
253
+ /** Evidence counts, memoised — several keys may share one successor. */
254
+ const evidence = /* @__PURE__ */ new Map();
255
+ const doomed = [];
256
+ for (const spec of present) {
257
+ let count = evidence.get(spec.successor);
258
+ if (count === void 0) {
259
+ try {
260
+ count = await deps.store.count.query({ collection: spec.successor });
261
+ } catch (err) {
262
+ deps.logger.warn("retired root key kept — its successor is unreadable", { meta: {
263
+ addonId: deps.addonId,
264
+ key: spec.key,
265
+ successor: spec.successor,
266
+ error: errText(err)
267
+ } });
268
+ count = 0;
269
+ }
270
+ evidence.set(spec.successor, count);
271
+ }
272
+ if (count === 0) {
273
+ deps.logger.warn("retired root key kept — its successor is EMPTY, migration has not run", { meta: {
274
+ addonId: deps.addonId,
275
+ key: spec.key,
276
+ successor: spec.successor
277
+ } });
278
+ continue;
279
+ }
280
+ doomed.push(spec);
281
+ }
282
+ if (doomed.length === 0) return [];
283
+ const dropped = new Set(doomed.map((spec) => spec.key));
284
+ const next = Object.fromEntries(Object.entries(row).filter(([key]) => !dropped.has(key)));
285
+ await deps.store.set.mutate({
286
+ namespace: deps.addonId,
287
+ collection: ADDON_SETTINGS_COLLECTION,
288
+ key: ADDON_SETTINGS_ROW,
289
+ value: next
290
+ });
291
+ const keys = doomed.map((spec) => spec.key);
292
+ deps.logger.info(`purged ${keys.length} retired settings key(s) from the root blob`, { meta: {
293
+ addonId: deps.addonId,
294
+ keys,
295
+ keptKeys: Object.keys(next),
296
+ reasons: doomed.map((spec) => `${spec.key}: ${spec.reason}`)
297
+ } });
298
+ return keys;
299
+ }
300
+ /** A store payload that is a plain object. A type guard, not a cast. */
301
+ function isRecord(value) {
302
+ return value !== null && typeof value === "object" && !Array.isArray(value);
303
+ }
304
+ function errText(err) {
305
+ return err instanceof Error ? err.message : String(err);
306
+ }
307
+ //#endregion
308
+ Object.defineProperty(exports, "RowMapStore", {
309
+ enumerable: true,
310
+ get: function() {
311
+ return RowMapStore;
312
+ }
313
+ });
314
+ Object.defineProperty(exports, "retireRootKeys", {
315
+ enumerable: true,
316
+ get: function() {
317
+ return retireRootKeys;
318
+ }
319
+ });
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_worker_protocol = require("../worker-protocol-BrlrMxXu.js");
3
- const require_lazy_sharp = require("../lazy-sharp-oYppDIZR.js");
2
+ const require_worker_protocol = require("../worker-protocol-BePduZVV.js");
3
+ const require_lazy_sharp = require("../lazy-sharp-RxUs6on_.js");
4
4
  //#region src/session-decode/color-conversion.ts
5
5
  /**
6
6
  * Explicit YUV→RGB colorspace/range resolution for the session-decode worker.
@@ -1,4 +1,4 @@
1
- import { i as formatNativeLeaseKnobs, n as isWorkerRequest, o as resolveNativeLeaseKnobs, r as logLevelForLine } from "../worker-protocol-DJWdPLKF.mjs";
1
+ import { i as formatNativeLeaseKnobs, n as isWorkerRequest, o as resolveNativeLeaseKnobs, r as logLevelForLine } from "../worker-protocol-DDpliBIW.mjs";
2
2
  import { n as setSharpWarnSink, r as hostExternalEntryUrls, t as getSharp } from "../lazy-sharp-6oymT_yf.mjs";
3
3
  //#region src/session-decode/color-conversion.ts
4
4
  /**
@@ -1,7 +1,7 @@
1
1
  import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, p as c, r as l, s as u, t as d, u as f } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DsIi4G5Q.mjs";
2
2
  import { a as p, i as m, n as h, o as g, r as _, t as v } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.js-C9j-2lBe.mjs";
3
3
  import { n as y, r as b, t as x } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-XO0-Pyu6.mjs";
4
- import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-zT-cYUdF.mjs";
4
+ import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DY31bUCj.mjs";
5
5
  import { n as C, t as w } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-BO7TIbJV.mjs";
6
6
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils.js
7
7
  var T = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), E = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), D = (e) => {
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.2.90",
21
+ version: "1.2.92",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_stream_broker_widgets",
@@ -33,7 +33,7 @@ var e = {
33
33
  },
34
34
  "@camstack/ui-library": {
35
35
  name: "@camstack/ui-library",
36
- version: "1.2.62",
36
+ version: "1.2.63",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_stream_broker_widgets",