@camstack/addon-pipeline-orchestrator 1.2.325 → 1.2.327
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/_stub.js +2 -2
- package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-DUstnfGf.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-CxcjyRWF.mjs} +2 -2
- package/dist/{_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-6CTaeZSL.mjs → _virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D0nHvaSr.mjs} +1 -1
- package/dist/{_virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-s9I9eY7F.mjs → _virtual_mf___mfe_internal__addon_pipeline_orchestrator_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DkOTHkEX.mjs} +1 -1
- package/dist/{hostInit-DMNJfKN-.mjs → hostInit-B3TkIIX_.mjs} +2 -2
- package/dist/index.js +302 -93
- package/dist/index.mjs +302 -93
- package/dist/remoteEntry.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5418,7 +5418,7 @@ var ZodIssueCode = {
|
|
|
5418
5418
|
var ZodFirstPartyTypeKind;
|
|
5419
5419
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5420
5420
|
//#endregion
|
|
5421
|
-
//#region ../types/dist/sleep-
|
|
5421
|
+
//#region ../types/dist/sleep-bwsTj221.mjs
|
|
5422
5422
|
/**
|
|
5423
5423
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5424
5424
|
* window to float samples (D455).
|
|
@@ -6685,7 +6685,6 @@ function createHandle(raw, opts) {
|
|
|
6685
6685
|
const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
|
|
6686
6686
|
const readAddonStoreResult = raw.readAddonStoreResult;
|
|
6687
6687
|
const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
|
|
6688
|
-
const readDeviceStoreBatch = raw.readDeviceStoreBatch;
|
|
6689
6688
|
const overrides = {
|
|
6690
6689
|
readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
|
|
6691
6690
|
...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
|
|
@@ -6709,12 +6708,12 @@ function createHandle(raw, opts) {
|
|
|
6709
6708
|
};
|
|
6710
6709
|
} } : {},
|
|
6711
6710
|
readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
|
|
6712
|
-
|
|
6713
|
-
const rows = await readDeviceStoreBatch
|
|
6711
|
+
readDeviceStoreBatch: async (deviceIds) => {
|
|
6712
|
+
const rows = await raw.readDeviceStoreBatch(deviceIds);
|
|
6714
6713
|
const out = /* @__PURE__ */ new Map();
|
|
6715
6714
|
for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
|
|
6716
6715
|
return out;
|
|
6717
|
-
}
|
|
6716
|
+
},
|
|
6718
6717
|
writeAddonStore: async (patch) => {
|
|
6719
6718
|
await writeAddon(patch);
|
|
6720
6719
|
},
|
|
@@ -7541,7 +7540,7 @@ var BaseAddon = class {
|
|
|
7541
7540
|
return this._addonSecretTree;
|
|
7542
7541
|
}
|
|
7543
7542
|
deviceSecretTree() {
|
|
7544
|
-
this._deviceSecretTree ??= secretTreeFromSchema(this.deviceSettingsSchema());
|
|
7543
|
+
this._deviceSecretTree ??= mergeSecretStatePaths(secretTreeFromSchema(this.deviceSettingsSchema()), this.secretStatePaths);
|
|
7545
7544
|
return this._deviceSecretTree;
|
|
7546
7545
|
}
|
|
7547
7546
|
/**
|
|
@@ -7617,42 +7616,135 @@ var BaseAddon = class {
|
|
|
7617
7616
|
this[timerOf] = timer;
|
|
7618
7617
|
}
|
|
7619
7618
|
/**
|
|
7620
|
-
* Wrap `ctx.settings.readAddonStore()` with a
|
|
7621
|
-
*
|
|
7622
|
-
* swap, or any race where the SqliteSettingsBackend is between shutdown and
|
|
7623
|
-
* re-initialize) doesn't propagate up into `initialize()` and leave the
|
|
7619
|
+
* Wrap `ctx.settings.readAddonStore()` with a retry budget so a transient
|
|
7620
|
+
* settings-store outage doesn't propagate up into `initialize()` and leave the
|
|
7624
7621
|
* addon permanently broken.
|
|
7625
7622
|
*
|
|
7626
|
-
*
|
|
7627
|
-
*
|
|
7628
|
-
* `
|
|
7623
|
+
* Two kinds of outage, two budgets:
|
|
7624
|
+
* - the store's own restart fingerprints (`SqliteSettingsBackend not
|
|
7625
|
+
* initialized`, `provider not available`): a short budget, then the addon
|
|
7626
|
+
* falls back to `{}` (defaults) - its first successful patch rehydrates;
|
|
7627
|
+
* - a TRANSPORT failure (the hub is restarting while this runner boots:
|
|
7628
|
+
* `discovery deadline exceeded`, `parent could not route`, a refused or
|
|
7629
|
+
* timed-out connection). Since D870 a runner's settings read THROWS instead
|
|
7630
|
+
* of answering `{}`, so without a budget a hub restart ends in a respawn
|
|
7631
|
+
* loop. A long, bounded budget; only after it the error propagates - the
|
|
7632
|
+
* addon never runs on defaults for a store it could not ask.
|
|
7633
|
+
* Anything else propagates immediately so real bugs surface.
|
|
7629
7634
|
*/
|
|
7630
7635
|
async readAddonStoreWithRetry() {
|
|
7631
7636
|
const settings = this.rawSettings();
|
|
7632
7637
|
if (!settings) return {};
|
|
7633
|
-
const delaysMs = [
|
|
7634
|
-
150,
|
|
7635
|
-
350,
|
|
7636
|
-
600,
|
|
7637
|
-
900
|
|
7638
|
-
];
|
|
7639
7638
|
let lastErr;
|
|
7640
|
-
for (let attempt = 0
|
|
7639
|
+
for (let attempt = 0;; attempt++) try {
|
|
7641
7640
|
const stored = await settings.readAddonStore() ?? {};
|
|
7642
7641
|
this.settingsStoreReady = true;
|
|
7643
7642
|
return stored;
|
|
7644
7643
|
} catch (err) {
|
|
7645
7644
|
lastErr = err;
|
|
7646
|
-
const
|
|
7647
|
-
if (
|
|
7648
|
-
|
|
7649
|
-
|
|
7645
|
+
const kind = classifyStoreError(err);
|
|
7646
|
+
if (kind === "permanent") throw err;
|
|
7647
|
+
const delay = (kind === "transport" ? TRANSPORT_RETRY_DELAYS_MS : RESTART_RETRY_DELAYS_MS)[attempt];
|
|
7648
|
+
if (delay === void 0) {
|
|
7649
|
+
if (kind === "transport") throw err;
|
|
7650
|
+
break;
|
|
7651
|
+
}
|
|
7652
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
7650
7653
|
}
|
|
7651
7654
|
this.settingsStoreReady = false;
|
|
7652
7655
|
this._ctx?.logger?.warn?.("readAddonStore: settings-store unavailable after retries — using defaults", { meta: { error: lastErr instanceof Error ? lastErr.message : String(lastErr) } });
|
|
7653
7656
|
return {};
|
|
7654
7657
|
}
|
|
7655
7658
|
};
|
|
7659
|
+
/** The settings store's own restart fingerprints. */
|
|
7660
|
+
var RESTART_RETRY_DELAYS_MS = [
|
|
7661
|
+
150,
|
|
7662
|
+
350,
|
|
7663
|
+
600,
|
|
7664
|
+
900
|
|
7665
|
+
];
|
|
7666
|
+
/** A hub restarting under a booting runner: ~100 s in total. */
|
|
7667
|
+
var TRANSPORT_RETRY_DELAYS_MS = [
|
|
7668
|
+
150,
|
|
7669
|
+
350,
|
|
7670
|
+
600,
|
|
7671
|
+
900,
|
|
7672
|
+
2e3,
|
|
7673
|
+
5e3,
|
|
7674
|
+
1e4,
|
|
7675
|
+
2e4,
|
|
7676
|
+
3e4,
|
|
7677
|
+
3e4
|
|
7678
|
+
];
|
|
7679
|
+
var RESTART_FINGERPRINTS = ["SqliteSettingsBackend not initialized", "provider not available"];
|
|
7680
|
+
/** Node / socket error codes of a connection that did not happen or broke. */
|
|
7681
|
+
var TRANSPORT_CODES = new Set([
|
|
7682
|
+
"ECONNREFUSED",
|
|
7683
|
+
"ECONNRESET",
|
|
7684
|
+
"ETIMEDOUT",
|
|
7685
|
+
"EPIPE",
|
|
7686
|
+
"EHOSTUNREACH",
|
|
7687
|
+
"ENETUNREACH"
|
|
7688
|
+
]);
|
|
7689
|
+
/** tRPC error codes that mean "the other side is not answering now", not "your call is wrong". */
|
|
7690
|
+
var UNAVAILABLE_TRPC_CODES = new Set([
|
|
7691
|
+
"TIMEOUT",
|
|
7692
|
+
"SERVICE_UNAVAILABLE",
|
|
7693
|
+
"BAD_GATEWAY",
|
|
7694
|
+
"GATEWAY_TIMEOUT"
|
|
7695
|
+
]);
|
|
7696
|
+
/** The exact phrases OUR transport raises when it cannot reach the hub. */
|
|
7697
|
+
var TRANSPORT_PHRASES = [
|
|
7698
|
+
"discovery deadline exceeded",
|
|
7699
|
+
"parent could not route child unowned cap call",
|
|
7700
|
+
"UDS request timed out after",
|
|
7701
|
+
"channel closed"
|
|
7702
|
+
];
|
|
7703
|
+
/** Moleculer error `type`s of a call that could not be served right now. */
|
|
7704
|
+
var MOLECULER_TRANSIENT_TYPES = new Set([
|
|
7705
|
+
"REQUEST_TIMEOUT",
|
|
7706
|
+
"SERVICE_NOT_FOUND",
|
|
7707
|
+
"SERVICE_NOT_AVAILABLE",
|
|
7708
|
+
"REQUEST_REJECTED",
|
|
7709
|
+
"BROKER_DISCONNECTED"
|
|
7710
|
+
]);
|
|
7711
|
+
function stringProp(value, key) {
|
|
7712
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
7713
|
+
const found = Reflect.get(value, key);
|
|
7714
|
+
return typeof found === "string" ? found : void 0;
|
|
7715
|
+
}
|
|
7716
|
+
/** The code an error carries: its own, its cause's, or a tRPC `data.code`. */
|
|
7717
|
+
function errorCode(err) {
|
|
7718
|
+
const out = {};
|
|
7719
|
+
const cause = typeof err === "object" && err !== null ? Reflect.get(err, "cause") : void 0;
|
|
7720
|
+
const data = typeof err === "object" && err !== null ? Reflect.get(err, "data") : void 0;
|
|
7721
|
+
const node = stringProp(err, "code") ?? stringProp(cause, "code");
|
|
7722
|
+
if (node !== void 0) out.node = node;
|
|
7723
|
+
const trpc = stringProp(data, "code");
|
|
7724
|
+
if (trpc !== void 0) out.trpc = trpc;
|
|
7725
|
+
return out;
|
|
7726
|
+
}
|
|
7727
|
+
/**
|
|
7728
|
+
* Is a settings read failure worth retrying, and with which budget? Only
|
|
7729
|
+
* EXPLICIT signals count: a socket error code, a tRPC "unavailable" code, or
|
|
7730
|
+
* one of the exact phrases our own transport raises. A message that merely
|
|
7731
|
+
* contains "timed out" or "unreachable" is not evidence of anything, and
|
|
7732
|
+
* `settings store not wired` is a wiring bug that retrying cannot fix.
|
|
7733
|
+
*/
|
|
7734
|
+
function classifyStoreError(err) {
|
|
7735
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7736
|
+
if (RESTART_FINGERPRINTS.some((f) => message.includes(f))) return "restart";
|
|
7737
|
+
const code = errorCode(err);
|
|
7738
|
+
if (code.node !== void 0 && TRANSPORT_CODES.has(code.node)) return "transport";
|
|
7739
|
+
if (code.trpc !== void 0 && UNAVAILABLE_TRPC_CODES.has(code.trpc)) return "transport";
|
|
7740
|
+
if (typeof err === "object" && err !== null) {
|
|
7741
|
+
if (Reflect.get(err, "retryable") === true) return "transport";
|
|
7742
|
+
const type = stringProp(err, "type");
|
|
7743
|
+
if (type !== void 0 && MOLECULER_TRANSIENT_TYPES.has(type)) return "transport";
|
|
7744
|
+
}
|
|
7745
|
+
if (TRANSPORT_PHRASES.some((f) => message.includes(f))) return "transport";
|
|
7746
|
+
return "permanent";
|
|
7747
|
+
}
|
|
7656
7748
|
/**
|
|
7657
7749
|
* Collect the keys of every field marked `perNode: true`, recursing into
|
|
7658
7750
|
* layout containers (`group` fields and `sub-tabs` tabs) the same way
|
|
@@ -12798,11 +12890,10 @@ var ClipModelMetaSchema = object({
|
|
|
12798
12890
|
/**
|
|
12799
12891
|
* The `vector-store` index this model's vectors live in. ONE feature space
|
|
12800
12892
|
* per index: two models share an index only when their vectors are ranked
|
|
12801
|
-
* against each other, which for CLIP means never across families.
|
|
12802
|
-
*
|
|
12803
|
-
* `modelId`); any other model gets its own.
|
|
12893
|
+
* against each other, which for CLIP means never across families. Every
|
|
12894
|
+
* model gets its own.
|
|
12804
12895
|
*/
|
|
12805
|
-
vectorIndex: string().regex(/^object-clip
|
|
12896
|
+
vectorIndex: string().regex(/^object-clip-[a-z0-9][a-z0-9-]*$/),
|
|
12806
12897
|
/**
|
|
12807
12898
|
* The text encoder's tokenizer, a declared SIBLING file of its onnx. The name
|
|
12808
12899
|
* must be unique per tokenizer: every model's siblings land in ONE flat
|
|
@@ -16974,6 +17065,28 @@ id: union([string(), number()]) }), {
|
|
|
16974
17065
|
kind: "mutation",
|
|
16975
17066
|
auth: "admin"
|
|
16976
17067
|
});
|
|
17068
|
+
/** One table of the settings database. */
|
|
17069
|
+
var StorageTableSchema = object({
|
|
17070
|
+
/** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
|
|
17071
|
+
name: string(),
|
|
17072
|
+
/** The owner prefix of a scoped name (`recorder` for `recorder:addon-settings`), else null. */
|
|
17073
|
+
owner: string().nullable(),
|
|
17074
|
+
rows: number().int().nonnegative(),
|
|
17075
|
+
/** Bytes the table's pages occupy (`dbstat`); null when the engine cannot measure. */
|
|
17076
|
+
bytes: number().int().nonnegative().nullable(),
|
|
17077
|
+
/** Declared by an addon since this engine process started (a running addon claims it). */
|
|
17078
|
+
declaredThisBoot: boolean()
|
|
17079
|
+
});
|
|
17080
|
+
method(object({ ownerPrefix: string().min(1).optional() }), object({ tables: array(StorageTableSchema) }), { auth: "admin" }), method(object({
|
|
17081
|
+
collection: string().min(1),
|
|
17082
|
+
expectRows: number().int().nonnegative()
|
|
17083
|
+
}), object({
|
|
17084
|
+
dropped: literal(true),
|
|
17085
|
+
rows: number().int().nonnegative()
|
|
17086
|
+
}), {
|
|
17087
|
+
kind: "mutation",
|
|
17088
|
+
auth: "admin"
|
|
17089
|
+
});
|
|
16977
17090
|
/**
|
|
16978
17091
|
* `keychain` — the hub's encrypted store of secret values (D787).
|
|
16979
17092
|
*
|
|
@@ -17515,6 +17628,16 @@ method(object({}), KeychainStatusSchema, { auth: "admin" }), method(object({}),
|
|
|
17515
17628
|
kind: "mutation",
|
|
17516
17629
|
auth: "admin",
|
|
17517
17630
|
caller: "required"
|
|
17631
|
+
}), method(object({
|
|
17632
|
+
ref: string().min(1),
|
|
17633
|
+
value: string().min(1)
|
|
17634
|
+
}), object({
|
|
17635
|
+
ref: string(),
|
|
17636
|
+
version: number().int()
|
|
17637
|
+
}), {
|
|
17638
|
+
kind: "mutation",
|
|
17639
|
+
auth: "admin",
|
|
17640
|
+
caller: "required"
|
|
17518
17641
|
}), method(object({ refs: array(string().min(1)).max(1e3) }), object({
|
|
17519
17642
|
deleted: array(string()).readonly(),
|
|
17520
17643
|
kept: array(object({
|
|
@@ -56730,7 +56853,19 @@ var TlsStatusSchema = object({
|
|
|
56730
56853
|
sans: array(string()),
|
|
56731
56854
|
caCertPem: string().nullable(),
|
|
56732
56855
|
reissueError: string().nullable(),
|
|
56733
|
-
restartRequired: boolean()
|
|
56856
|
+
restartRequired: boolean(),
|
|
56857
|
+
/** An uploaded certificate is served but no CA is kept for it and it is not publicly signed (D873). */
|
|
56858
|
+
caMissing: boolean(),
|
|
56859
|
+
/** Does the hub's own generated CA carry a critical name constraint? `null` while it has none. */
|
|
56860
|
+
localCaConstrained: boolean().nullable(),
|
|
56861
|
+
/** ISO time the generated CA was replaced by a constrained one; devices must re-download it. */
|
|
56862
|
+
caReplacedAt: string().nullable(),
|
|
56863
|
+
/** The uploaded certificate kept on disk, selectable again after "Revert to generated". */
|
|
56864
|
+
storedUpload: object({
|
|
56865
|
+
leafFingerprintSha256: string(),
|
|
56866
|
+
validTo: string(),
|
|
56867
|
+
hasCa: boolean()
|
|
56868
|
+
}).nullable()
|
|
56734
56869
|
});
|
|
56735
56870
|
method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
|
|
56736
56871
|
/**
|
|
@@ -56786,6 +56921,9 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
56786
56921
|
}), method(_void(), object({ pem: string() })), method(_void(), TlsStatusSchema, {
|
|
56787
56922
|
kind: "mutation",
|
|
56788
56923
|
auth: "admin"
|
|
56924
|
+
}), method(_void(), TlsStatusSchema, {
|
|
56925
|
+
kind: "mutation",
|
|
56926
|
+
auth: "admin"
|
|
56789
56927
|
});
|
|
56790
56928
|
object({
|
|
56791
56929
|
/** Lifecycle state of the lock. `jammed` means the motor reported
|
|
@@ -65921,6 +66059,12 @@ Object.freeze({
|
|
|
65921
66059
|
addonId: null,
|
|
65922
66060
|
access: "create"
|
|
65923
66061
|
},
|
|
66062
|
+
"keychainAdmin.importSecret": {
|
|
66063
|
+
capName: "keychain-admin",
|
|
66064
|
+
capScope: "system",
|
|
66065
|
+
addonId: null,
|
|
66066
|
+
access: "create"
|
|
66067
|
+
},
|
|
65924
66068
|
"keychainAdmin.listAudit": {
|
|
65925
66069
|
capName: "keychain-admin",
|
|
65926
66070
|
capScope: "system",
|
|
@@ -66317,6 +66461,12 @@ Object.freeze({
|
|
|
66317
66461
|
addonId: null,
|
|
66318
66462
|
access: "create"
|
|
66319
66463
|
},
|
|
66464
|
+
"localNetwork.useStoredUploadedCertificate": {
|
|
66465
|
+
capName: "local-network",
|
|
66466
|
+
capScope: "system",
|
|
66467
|
+
addonId: null,
|
|
66468
|
+
access: "create"
|
|
66469
|
+
},
|
|
66320
66470
|
"lockControl.getStatus": {
|
|
66321
66471
|
capName: "lock-control",
|
|
66322
66472
|
capScope: "device",
|
|
@@ -69701,6 +69851,18 @@ Object.freeze({
|
|
|
69701
69851
|
addonId: null,
|
|
69702
69852
|
access: "create"
|
|
69703
69853
|
},
|
|
69854
|
+
"storageAdmin.dropCollection": {
|
|
69855
|
+
capName: "storage-admin",
|
|
69856
|
+
capScope: "system",
|
|
69857
|
+
addonId: null,
|
|
69858
|
+
access: "delete"
|
|
69859
|
+
},
|
|
69860
|
+
"storageAdmin.listTables": {
|
|
69861
|
+
capName: "storage-admin",
|
|
69862
|
+
capScope: "system",
|
|
69863
|
+
addonId: null,
|
|
69864
|
+
access: "view"
|
|
69865
|
+
},
|
|
69704
69866
|
"storageEvictable.evict": {
|
|
69705
69867
|
capName: "storage-evictable",
|
|
69706
69868
|
capScope: "system",
|
|
@@ -74746,7 +74908,11 @@ var REMOVED_MODEL_IDS = {
|
|
|
74746
74908
|
"face-embedding": ["arcface-r100", "inception-resnet-v1"],
|
|
74747
74909
|
"plate-detection": ["yolov8n-plate"],
|
|
74748
74910
|
"plate-ocr": ["vgg-english-g2"],
|
|
74749
|
-
"clip-embedding": [
|
|
74911
|
+
"clip-embedding": [
|
|
74912
|
+
"mobileclip-s0",
|
|
74913
|
+
"mobileclip-s1",
|
|
74914
|
+
"mobileclip-s2"
|
|
74915
|
+
],
|
|
74750
74916
|
"package-detection": ["yolov8n-package"],
|
|
74751
74917
|
"bird-classifier": ["bird-classifier-aiy-edgetpu", "bird-nabirds-404"],
|
|
74752
74918
|
"animal-classifier": ["animals-10"],
|
|
@@ -76643,6 +76809,22 @@ var DurableLedger = class DurableLedger {
|
|
|
76643
76809
|
}
|
|
76644
76810
|
}
|
|
76645
76811
|
/**
|
|
76812
|
+
* Drop every MIRRORED row about `deviceId` (per the spec's `deviceIdOf`),
|
|
76813
|
+
* leaving the durable rows to the caller's `deleteWhere` (D871). Returns how
|
|
76814
|
+
* many mirror entries went; a spec without `deviceIdOf` evicts nothing.
|
|
76815
|
+
*/
|
|
76816
|
+
evictDevice(deviceId) {
|
|
76817
|
+
const of = this.spec.deviceIdOf;
|
|
76818
|
+
if (of === void 0) return 0;
|
|
76819
|
+
let evicted = 0;
|
|
76820
|
+
for (const [key, row] of [...this.mirror.entries()]) {
|
|
76821
|
+
if (of(row) !== deviceId) continue;
|
|
76822
|
+
this.mirror.delete(key);
|
|
76823
|
+
evicted += 1;
|
|
76824
|
+
}
|
|
76825
|
+
return evicted;
|
|
76826
|
+
}
|
|
76827
|
+
/**
|
|
76646
76828
|
* Drop every mirrored key NOT in `activeKeys`. Returns how many rows went.
|
|
76647
76829
|
*
|
|
76648
76830
|
* **The caller must hold an AUTHORITATIVE active set.** A prune driven by a
|
|
@@ -83974,15 +84156,10 @@ var DeviceFeaturesMirror = class {
|
|
|
83974
84156
|
async hydrate() {
|
|
83975
84157
|
const settings = this.settings;
|
|
83976
84158
|
if (settings === null) return 0;
|
|
83977
|
-
const
|
|
83978
|
-
|
|
83979
|
-
const
|
|
83980
|
-
|
|
83981
|
-
const blobs = batch === void 0 ? new Map(await Promise.all(ids.map(async (id) => [id, await settings.readDeviceStore(id)]))) : await batch.call(settings, ids);
|
|
83982
|
-
for (const [deviceId, blob] of blobs) {
|
|
83983
|
-
const row = rowOf(deviceId, blob[DEVICE_FEATURES_KEY]);
|
|
83984
|
-
if (row !== null) this.local.set(deviceId, row);
|
|
83985
|
-
}
|
|
84159
|
+
const ids = await settings.listDeviceStoreIds();
|
|
84160
|
+
for (const [deviceId, blob] of await settings.readDeviceStoreBatch(ids)) {
|
|
84161
|
+
const row = rowOf(deviceId, blob[DEVICE_FEATURES_KEY]);
|
|
84162
|
+
if (row !== null) this.local.set(deviceId, row);
|
|
83986
84163
|
}
|
|
83987
84164
|
this.logger.info("device-features mirror restored", { meta: { devices: this.local.size } });
|
|
83988
84165
|
return this.local.size;
|
|
@@ -87640,25 +87817,33 @@ var NodeStressController = class NodeStressController {
|
|
|
87640
87817
|
//#region src/durable/lts-aggregator.ts
|
|
87641
87818
|
/** Wall-clock bucket width. Five minutes, matching HA's statistics tier. */
|
|
87642
87819
|
var LTS_BUCKET_MS = 5 * 6e4;
|
|
87820
|
+
/** Buckets written per transaction — bounds one statement, not the flush. */
|
|
87821
|
+
var FLUSH_BATCH = 200;
|
|
87643
87822
|
/** How often the row cap is enforced. Rarely: it is a bound, not a deadline. */
|
|
87644
87823
|
var CAP_SWEEP_INTERVAL_MS = 6 * 36e5;
|
|
87824
|
+
/**
|
|
87825
|
+
* The row's identity IS its four key columns (D871): a composite
|
|
87826
|
+
* `primaryKey` + `WITHOUT ROWID`, so the table is its own index and there is no
|
|
87827
|
+
* TEXT `id` repeating what the row already says. The old layout kept
|
|
87828
|
+
* `<subject>|<series>|<scope>|<bucketStart>` as a TEXT primary key beside the
|
|
87829
|
+
* four columns — 85 MB of rows plus a 44 MB autoindex for 1.07 M rows. A
|
|
87830
|
+
* composite-key collection is written with `upsertMany` (a bucket flushed twice
|
|
87831
|
+
* replaces itself) and read with `query` / `count` / `deleteWhere`; the calls
|
|
87832
|
+
* that address one row by id are refused by the engine.
|
|
87833
|
+
*/
|
|
87834
|
+
var LTS_KEY_COLUMNS = [
|
|
87835
|
+
"subject",
|
|
87836
|
+
"series",
|
|
87837
|
+
"scope",
|
|
87838
|
+
"bucketStart"
|
|
87839
|
+
];
|
|
87645
87840
|
var LTS_COLUMNS = [
|
|
87646
|
-
(
|
|
87647
|
-
/** `<subject>|<series>|<scope>|<bucketStart>` — deterministic, so a bucket
|
|
87648
|
-
* flushed twice replaces itself rather than doubling. */
|
|
87649
|
-
{
|
|
87650
|
-
name: "id",
|
|
87651
|
-
type: "TEXT",
|
|
87652
|
-
primaryKey: true,
|
|
87653
|
-
notNull: true
|
|
87654
|
-
}),
|
|
87655
87841
|
(
|
|
87656
87842
|
/** The camera id, or the node id. One column, because every query is
|
|
87657
87843
|
* "this thing over time" and the thing is one or the other. */
|
|
87658
87844
|
{
|
|
87659
87845
|
name: "subject",
|
|
87660
|
-
type: "TEXT"
|
|
87661
|
-
notNull: true
|
|
87846
|
+
type: "TEXT"
|
|
87662
87847
|
}),
|
|
87663
87848
|
(
|
|
87664
87849
|
/** Numeric mirror of `subject` for camera rows, so a per-camera question is
|
|
@@ -87670,22 +87855,19 @@ var LTS_COLUMNS = [
|
|
|
87670
87855
|
}),
|
|
87671
87856
|
{
|
|
87672
87857
|
name: "series",
|
|
87673
|
-
type: "TEXT"
|
|
87674
|
-
notNull: true
|
|
87858
|
+
type: "TEXT"
|
|
87675
87859
|
},
|
|
87676
87860
|
(
|
|
87677
87861
|
/** Sub-scope within a series: a zoneId for occupancy, `''` otherwise. */
|
|
87678
87862
|
{
|
|
87679
87863
|
name: "scope",
|
|
87680
|
-
type: "TEXT"
|
|
87681
|
-
notNull: true
|
|
87864
|
+
type: "TEXT"
|
|
87682
87865
|
}),
|
|
87683
87866
|
(
|
|
87684
87867
|
/** Bucket start, wall-clock aligned to {@link LTS_BUCKET_MS}. */
|
|
87685
87868
|
{
|
|
87686
87869
|
name: "bucketStart",
|
|
87687
|
-
type: "INTEGER"
|
|
87688
|
-
notNull: true
|
|
87870
|
+
type: "INTEGER"
|
|
87689
87871
|
}),
|
|
87690
87872
|
{
|
|
87691
87873
|
name: "samples",
|
|
@@ -87708,12 +87890,15 @@ var LTS_COLUMNS = [
|
|
|
87708
87890
|
notNull: true
|
|
87709
87891
|
}
|
|
87710
87892
|
];
|
|
87893
|
+
/**
|
|
87894
|
+
* The primary key serves every per-subject read (`subject, series, scope` then a
|
|
87895
|
+
* `bucketStart` range, already in bucket order); this index serves the read that
|
|
87896
|
+
* names a series without a subject. The old `deviceId` index went with the TEXT
|
|
87897
|
+
* id: nothing queries a series by the numeric mirror.
|
|
87898
|
+
*/
|
|
87711
87899
|
var LTS_INDEXES = [{
|
|
87712
87900
|
name: "idx_lts_series_bucket",
|
|
87713
87901
|
columns: ["series", "bucketStart"]
|
|
87714
|
-
}, {
|
|
87715
|
-
name: "idx_lts_device",
|
|
87716
|
-
columns: ["deviceId"]
|
|
87717
87902
|
}];
|
|
87718
87903
|
function ltsBucketStart(at, bucketMs = LTS_BUCKET_MS) {
|
|
87719
87904
|
return Math.floor(at / bucketMs) * bucketMs;
|
|
@@ -87742,6 +87927,8 @@ var LtsAggregator = class {
|
|
|
87742
87927
|
return store.declareCollection.mutate({
|
|
87743
87928
|
collection,
|
|
87744
87929
|
columns: [...LTS_COLUMNS],
|
|
87930
|
+
primaryKey: [...LTS_KEY_COLUMNS],
|
|
87931
|
+
withoutRowid: true,
|
|
87745
87932
|
indexes: [...LTS_INDEXES]
|
|
87746
87933
|
});
|
|
87747
87934
|
}
|
|
@@ -87801,43 +87988,60 @@ var LtsAggregator = class {
|
|
|
87801
87988
|
return 0;
|
|
87802
87989
|
}
|
|
87803
87990
|
let written = 0;
|
|
87804
|
-
for (const
|
|
87805
|
-
|
|
87806
|
-
|
|
87807
|
-
await this.store.set.mutate({
|
|
87808
|
-
collection: this.collection,
|
|
87809
|
-
key,
|
|
87810
|
-
value: {
|
|
87811
|
-
subject: bucket.subject,
|
|
87812
|
-
...bucket.deviceId !== void 0 ? { deviceId: bucket.deviceId } : {},
|
|
87813
|
-
series: bucket.series,
|
|
87814
|
-
scope: bucket.scope,
|
|
87815
|
-
bucketStart: bucket.bucketStart,
|
|
87816
|
-
samples: bucket.samples,
|
|
87817
|
-
sum: bucket.sum,
|
|
87818
|
-
min: bucket.min,
|
|
87819
|
-
max: bucket.max
|
|
87820
|
-
}
|
|
87821
|
-
});
|
|
87822
|
-
written += 1;
|
|
87823
|
-
this.groups.add(groupKey(bucket.subject, bucket.series, bucket.scope));
|
|
87824
|
-
} catch (err) {
|
|
87825
|
-
this.logger.warn("lts bucket write failed — this interval will be missing", {
|
|
87826
|
-
...bucket.deviceId !== void 0 ? { tags: { deviceId: bucket.deviceId } } : {},
|
|
87827
|
-
meta: {
|
|
87828
|
-
collection: this.collection,
|
|
87829
|
-
series: bucket.series,
|
|
87830
|
-
subject: bucket.subject,
|
|
87831
|
-
bucketStart: bucket.bucketStart,
|
|
87832
|
-
error: String(err)
|
|
87833
|
-
}
|
|
87834
|
-
});
|
|
87835
|
-
}
|
|
87836
|
-
this.open.delete(key);
|
|
87991
|
+
for (const batch of chunk(due, FLUSH_BATCH)) {
|
|
87992
|
+
written += await this.writeBatch(batch);
|
|
87993
|
+
for (const bucket of batch) this.open.delete(rowId(bucket.subject, bucket.series, bucket.scope, bucket.bucketStart));
|
|
87837
87994
|
}
|
|
87838
87995
|
await this.maybeSweepCap(now);
|
|
87839
87996
|
return written;
|
|
87840
87997
|
}
|
|
87998
|
+
/**
|
|
87999
|
+
* Upsert one batch in a single transaction. If the batch is refused as a
|
|
88000
|
+
* whole (all-or-nothing), each bucket is retried alone so one bad row costs
|
|
88001
|
+
* one interval, not forty. Returns the number of rows written.
|
|
88002
|
+
*/
|
|
88003
|
+
async writeBatch(batch) {
|
|
88004
|
+
try {
|
|
88005
|
+
await this.upsert(batch);
|
|
88006
|
+
for (const bucket of batch) this.groups.add(groupKey(bucket.subject, bucket.series, bucket.scope));
|
|
88007
|
+
return batch.length;
|
|
88008
|
+
} catch (err) {
|
|
88009
|
+
if (batch.length > 1) {
|
|
88010
|
+
let written = 0;
|
|
88011
|
+
for (const bucket of batch) written += await this.writeBatch([bucket]);
|
|
88012
|
+
return written;
|
|
88013
|
+
}
|
|
88014
|
+
const bucket = batch[0];
|
|
88015
|
+
this.logger.warn("lts bucket write failed — this interval will be missing", {
|
|
88016
|
+
...bucket?.deviceId !== void 0 ? { tags: { deviceId: bucket.deviceId } } : {},
|
|
88017
|
+
meta: {
|
|
88018
|
+
collection: this.collection,
|
|
88019
|
+
series: bucket?.series,
|
|
88020
|
+
subject: bucket?.subject,
|
|
88021
|
+
bucketStart: bucket?.bucketStart,
|
|
88022
|
+
error: String(err)
|
|
88023
|
+
}
|
|
88024
|
+
});
|
|
88025
|
+
return 0;
|
|
88026
|
+
}
|
|
88027
|
+
}
|
|
88028
|
+
async upsert(batch) {
|
|
88029
|
+
await this.store.upsertMany.mutate({
|
|
88030
|
+
collection: this.collection,
|
|
88031
|
+
conflict: [...LTS_KEY_COLUMNS],
|
|
88032
|
+
records: batch.map((bucket) => ({ data: {
|
|
88033
|
+
subject: bucket.subject,
|
|
88034
|
+
deviceId: bucket.deviceId ?? null,
|
|
88035
|
+
series: bucket.series,
|
|
88036
|
+
scope: bucket.scope,
|
|
88037
|
+
bucketStart: bucket.bucketStart,
|
|
88038
|
+
samples: bucket.samples,
|
|
88039
|
+
sum: bucket.sum,
|
|
88040
|
+
min: bucket.min,
|
|
88041
|
+
max: bucket.max
|
|
88042
|
+
} }))
|
|
88043
|
+
});
|
|
88044
|
+
}
|
|
87841
88045
|
/** Read closed buckets back. The read surface every chart will use. */
|
|
87842
88046
|
async read(query = {}) {
|
|
87843
88047
|
const where = {};
|
|
@@ -87922,6 +88126,11 @@ var LtsAggregator = class {
|
|
|
87922
88126
|
}
|
|
87923
88127
|
}
|
|
87924
88128
|
};
|
|
88129
|
+
function chunk(items, size) {
|
|
88130
|
+
const out = [];
|
|
88131
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
88132
|
+
return out;
|
|
88133
|
+
}
|
|
87925
88134
|
function groupKey(subject, series, scope) {
|
|
87926
88135
|
return `${subject}${series}${scope}`;
|
|
87927
88136
|
}
|
|
@@ -87980,7 +88189,7 @@ function recordToRow(data) {
|
|
|
87980
88189
|
* write="one row per (node, series, 5-min bucket), written ONCE when the bucket closes; a node reporting no samples writes nothing"
|
|
87981
88190
|
* retention="row CAP per (subject, series, scope) — 105,120 rows ≈ one year of 5-minute buckets. Not an age sweep: an LTS row summarises an interval and has no owner to be orphaned from."
|
|
87982
88191
|
*/
|
|
87983
|
-
var NODE_STRESS_LTS_COLLECTION = "pipeline-orchestrator:stats-5m";
|
|
88192
|
+
var NODE_STRESS_LTS_COLLECTION = "pipeline-orchestrator:stats-5m-v2";
|
|
87984
88193
|
var NodeStressLts = class {
|
|
87985
88194
|
lts;
|
|
87986
88195
|
constructor(deps) {
|