@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.
- package/dist/{addon-utils-eUS6n_Zj.js → addon-utils-CLc6yHCN.js} +1 -1
- package/dist/audio-analyzer/index.js +3 -3
- package/dist/audio-analyzer/index.mjs +2 -2
- package/dist/detection-pipeline/index.js +163 -42
- package/dist/detection-pipeline/index.mjs +161 -40
- package/dist/{dist-dm3t4BOt.js → dist-C11WuNUP.js} +87 -51
- package/dist/{dist-gXdWP96z.mjs → dist-Ccmt3fGJ.mjs} +82 -52
- package/dist/{event-loop-stall-monitor-Lki2_mYY.js → event-loop-stall-monitor-Cq_NeC4o.js} +175 -132
- package/dist/{event-loop-stall-monitor-TnQ21mu0.mjs → event-loop-stall-monitor-OJrOMeuu.mjs} +170 -133
- package/dist/{lazy-sharp-oYppDIZR.js → lazy-sharp-RxUs6on_.js} +1 -1
- package/dist/motion-wasm/index.js +2 -2
- package/dist/motion-wasm/index.mjs +1 -1
- package/dist/pipeline-runner/index.js +5 -4
- package/dist/pipeline-runner/index.mjs +4 -3
- package/dist/{process-memory-CKWh609B.mjs → process-memory-D0zDmXLI.mjs} +1 -1
- package/dist/{process-memory-vkFVOdkV.js → process-memory-DOjQ3MgC.js} +1 -1
- package/dist/recorder/index.js +333 -125
- package/dist/recorder/index.mjs +332 -124
- package/dist/retire-root-keys-DMolfhsP.mjs +308 -0
- package/dist/retire-root-keys-KE6D6Xh_.js +319 -0
- package/dist/session-decode/decode-worker-child.js +2 -2
- package/dist/session-decode/decode-worker-child.mjs +1 -1
- package/dist/stream-broker/_stub.js +1 -1
- 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
- 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
- package/dist/stream-broker/{hostInit-Cm2rl8-e.mjs → hostInit-Da9wVA2r.mjs} +2 -2
- package/dist/stream-broker/index.js +285 -41
- package/dist/stream-broker/index.mjs +285 -41
- package/dist/stream-broker/remoteEntry.js +1 -1
- package/dist/{worker-protocol-BrlrMxXu.js → worker-protocol-BePduZVV.js} +1 -1
- package/dist/{worker-protocol-DJWdPLKF.mjs → worker-protocol-DDpliBIW.mjs} +1 -1
- package/package.json +1 -1
- package/python/postprocessors/ctc.py +56 -3
- 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-
|
|
3
|
-
const require_lazy_sharp = require("../lazy-sharp-
|
|
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-
|
|
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-
|
|
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.
|
|
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.
|
|
36
|
+
version: "1.2.63",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_stream_broker_widgets",
|