@camstack/types 1.2.367 → 1.2.369

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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-NZlEFYGX.js");
2
+ const require_sleep = require("./sleep-CXlc9YJ5.js");
3
3
  const require_event_category = require("./event-category-T43rdQWR.js");
4
4
  const require_composition = require("./composition-DCYnv4I6.js");
5
5
  const require_node_color = require("./node-color.js");
@@ -13877,6 +13877,243 @@ var keychainCapability = {
13877
13877
  }
13878
13878
  };
13879
13879
  //#endregion
13880
+ //#region src/capabilities/device-history.cap.ts
13881
+ /**
13882
+ * device-history — the ONE mechanism for numeric history (D850).
13883
+ *
13884
+ * A device value is recorded because its capability says so (the `history`
13885
+ * block of a `*.cap.ts`, captured hub-side from `DeviceStateChanged`); any
13886
+ * other series is DECLARED by the addon that produces it and fed with points
13887
+ * (`record`) or closed five-minute buckets (`recordBuckets`). Rollups 5m / 1h /
13888
+ * 1d are computed here and nowhere else. Storage is `history.db` (D851).
13889
+ *
13890
+ * Who the PRODUCER is: `declareSeries`, `record` and `recordBuckets` are owner
13891
+ * checked against the `callerAddonId` the transport stamps into a call that
13892
+ * crossed a process (never in an input schema, so a session cannot supply it).
13893
+ * A call with no stamp is refused `not-owner`. The methods are `admin` so a
13894
+ * non-admin session never reaches them; an addon's `ctx.api` call bypasses the
13895
+ * tier by design.
13896
+ */
13897
+ /** Whose history: a real device, or a cluster node (load, memory). */
13898
+ var DeviceHistorySubjectSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
13899
+ kind: zod.z.literal("device"),
13900
+ deviceId: zod.z.number().int()
13901
+ }), zod.z.object({
13902
+ kind: zod.z.literal("node"),
13903
+ nodeId: zod.z.string().min(1)
13904
+ })]);
13905
+ var DeviceHistoryKindSchema = zod.z.enum([
13906
+ "gauge",
13907
+ "counter",
13908
+ "boolean"
13909
+ ]);
13910
+ var DeviceHistoryResolutionSchema = zod.z.enum([
13911
+ "raw",
13912
+ "5m",
13913
+ "1h",
13914
+ "1d"
13915
+ ]);
13916
+ /** Why a producer's point or bucket was not stored. Counted, never silent (D391). */
13917
+ var DeviceHistoryRefusalReasonSchema = zod.z.enum([
13918
+ "undeclared",
13919
+ "not-owner",
13920
+ "opted-out",
13921
+ "not-a-real-device",
13922
+ "rate",
13923
+ "ingest-mismatch",
13924
+ "out-of-range"
13925
+ ]);
13926
+ var RefusalSchema = zod.z.object({
13927
+ key: zod.z.string(),
13928
+ reason: DeviceHistoryRefusalReasonSchema,
13929
+ count: zod.z.number().int()
13930
+ });
13931
+ var IngestResultSchema = zod.z.object({
13932
+ accepted: zod.z.number().int(),
13933
+ refused: zod.z.array(RefusalSchema).readonly()
13934
+ });
13935
+ var SeriesInfoSchema = zod.z.object({
13936
+ key: zod.z.string(),
13937
+ scope: zod.z.string(),
13938
+ kind: DeviceHistoryKindSchema,
13939
+ /** `null` is unknown (a `unitFrom` series whose device does not state a unit right now), never a guess. */
13940
+ unit: zod.z.string().nullable(),
13941
+ displayName: zod.z.string(),
13942
+ source: zod.z.enum(["cap", "addon"]),
13943
+ firstTs: zod.z.number(),
13944
+ lastTs: zod.z.number()
13945
+ });
13946
+ var QueryPointSchema = zod.z.object({
13947
+ /** Raw: the point's time. Rollup: the bucket start. */
13948
+ t: zod.z.number(),
13949
+ samples: zod.z.number().int(),
13950
+ /** `sum / samples`. */
13951
+ mean: zod.z.number().nullable(),
13952
+ /** Time-weighted mean (`tw_sum / tw_ms`); `null` for bucket-ingested series. For a boolean: the fraction of time true. */
13953
+ twMean: zod.z.number().nullable(),
13954
+ min: zod.z.number(),
13955
+ max: zod.z.number(),
13956
+ /** `null` is unknown, never zero (D393). */
13957
+ last: zod.z.number().nullable()
13958
+ });
13959
+ var QuerySeriesSchema = zod.z.object({
13960
+ scope: zod.z.string(),
13961
+ /** The last point STRICTLY BEFORE `from` in the chosen tier, so a step chart can start at the held value. */
13962
+ before: zod.z.object({
13963
+ t: zod.z.number(),
13964
+ value: zod.z.number().nullable()
13965
+ }).nullable(),
13966
+ points: zod.z.array(QueryPointSchema).readonly()
13967
+ });
13968
+ /** The window part of a query, shared by the device and node forms. */
13969
+ var QueryWindowShape = {
13970
+ key: zod.z.string(),
13971
+ scope: zod.z.string().optional(),
13972
+ from: zod.z.number(),
13973
+ to: zod.z.number(),
13974
+ resolution: zod.z.union([DeviceHistoryResolutionSchema, zod.z.literal("auto")]).default("auto"),
13975
+ maxPoints: zod.z.number().int().positive().max(5e3).default(1e3)
13976
+ };
13977
+ var QueryResultSchema = zod.z.object({
13978
+ resolution: DeviceHistoryResolutionSchema,
13979
+ series: zod.z.array(QuerySeriesSchema).readonly(),
13980
+ truncated: zod.z.boolean()
13981
+ });
13982
+ var TierStatusSchema = zod.z.object({
13983
+ tier: DeviceHistoryResolutionSchema,
13984
+ /** `null` until the first background count answered. */
13985
+ rows: zod.z.number().int().nullable(),
13986
+ /** `rows` times a fixed per-row estimate — approximate, labelled so. */
13987
+ approxBytes: zod.z.number().nullable(),
13988
+ countedAt: zod.z.number().nullable()
13989
+ });
13990
+ /** Operator/diagnostic view: rows per tier, last runs, refusals and drops since boot. */
13991
+ var DeviceHistoryStatusSchema = zod.z.object({
13992
+ startedAt: zod.z.number(),
13993
+ /** The store is declared, series loaded and the first reconcile has answered. */
13994
+ ready: zod.z.boolean(),
13995
+ series: zod.z.number().int(),
13996
+ metrics: zod.z.number().int(),
13997
+ optedOutDevices: zod.z.number().int(),
13998
+ tiers: zod.z.array(TierStatusSchema).readonly(),
13999
+ capture: zod.z.object({
14000
+ eventsSeen: zod.z.number().int(),
14001
+ pointsEmitted: zod.z.number().int(),
14002
+ pointsWritten: zod.z.number().int(),
14003
+ pointsDropped: zod.z.number().int(),
14004
+ bufferDepth: zod.z.number().int(),
14005
+ trailingEmitted: zod.z.number().int(),
14006
+ reconciledPoints: zod.z.number().int(),
14007
+ reconcileRuns: zod.z.number().int(),
14008
+ reconcileFailures: zod.z.number().int(),
14009
+ lastReconcileAt: zod.z.number().nullable(),
14010
+ lastFlushAt: zod.z.number().nullable(),
14011
+ flushFailures: zod.z.number().int(),
14012
+ skipped: zod.z.record(zod.z.string(), zod.z.number().int())
14013
+ }),
14014
+ ingest: zod.z.object({
14015
+ accepted: zod.z.number().int(),
14016
+ refused: zod.z.record(zod.z.string(), zod.z.number().int())
14017
+ }),
14018
+ rollup: zod.z.object({
14019
+ last5mAt: zod.z.number().nullable(),
14020
+ last1hAt: zod.z.number().nullable(),
14021
+ last1dAt: zod.z.number().nullable(),
14022
+ errors: zod.z.number().int(),
14023
+ dirty5m: zod.z.number().int(),
14024
+ dirty1h: zod.z.number().int(),
14025
+ dirty1d: zod.z.number().int()
14026
+ }),
14027
+ retention: zod.z.object({
14028
+ lastRunAt: zod.z.number().nullable(),
14029
+ deleted: zod.z.record(zod.z.string(), zod.z.number().int()),
14030
+ purgedSeries: zod.z.number().int()
14031
+ }),
14032
+ directory: zod.z.object({
14033
+ devices: zod.z.number().int(),
14034
+ lastRefreshAt: zod.z.number().nullable()
14035
+ })
14036
+ });
14037
+ var deviceHistoryCapability = {
14038
+ name: "device-history",
14039
+ scope: "system",
14040
+ mode: "singleton",
14041
+ methods: {
14042
+ /**
14043
+ * The series a DEVICE has, and their span. `deviceId` is a flat top-level field
14044
+ * on purpose: the generated device selector (and so the scope gate) only sees
14045
+ * top-level ids, and a nested `subject.deviceId` would let a principal limited
14046
+ * to one device read another's history. Node series are the admin-only
14047
+ * `listNodeSeries` / `queryNode`.
14048
+ */
14049
+ listSeries: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int() }), zod.z.array(SeriesInfoSchema).readonly()),
14050
+ listNodeSeries: require_sleep.method(zod.z.object({ nodeId: zod.z.string().min(1) }), zod.z.array(SeriesInfoSchema).readonly(), { auth: "admin" }),
14051
+ /**
14052
+ * One series (or every scope of it) at one resolution. `auto` picks the
14053
+ * finest tier whose retention covers `from` and whose point count fits
14054
+ * `maxPoints`; when even the coarsest tier does not fit, the newest
14055
+ * `maxPoints` points are returned and `truncated` is true.
14056
+ */
14057
+ query: require_sleep.method(zod.z.object({
14058
+ deviceId: zod.z.number().int(),
14059
+ ...QueryWindowShape
14060
+ }), QueryResultSchema),
14061
+ queryNode: require_sleep.method(zod.z.object({
14062
+ nodeId: zod.z.string().min(1),
14063
+ ...QueryWindowShape
14064
+ }), QueryResultSchema, { auth: "admin" }),
14065
+ /** Producer: declare (idempotent) a series key this addon owns: `addon:<callerAddonId>/<name>`. */
14066
+ declareSeries: require_sleep.method(zod.z.object({
14067
+ name: zod.z.string().regex(/^[a-z0-9-]+$/),
14068
+ subjectKind: zod.z.enum(["device", "node"]),
14069
+ kind: DeviceHistoryKindSchema,
14070
+ unit: zod.z.string().optional(),
14071
+ displayName: zod.z.string().min(1),
14072
+ ingest: zod.z.enum(["points", "buckets"])
14073
+ }), zod.z.object({ key: zod.z.string() }), {
14074
+ kind: "mutation",
14075
+ auth: "admin"
14076
+ }),
14077
+ /** Producer: points. Low rate: at most one per 10 s per series, sustained. */
14078
+ record: require_sleep.method(zod.z.object({ points: zod.z.array(zod.z.object({
14079
+ key: zod.z.string(),
14080
+ subject: DeviceHistorySubjectSchema,
14081
+ scope: zod.z.string().default(""),
14082
+ ts: zod.z.number(),
14083
+ value: zod.z.number()
14084
+ })).max(5e3) }), IngestResultSchema, {
14085
+ kind: "mutation",
14086
+ auth: "admin"
14087
+ }),
14088
+ /** Producer: CLOSED five-minute buckets. Idempotent upsert keyed (series, bucketStart). */
14089
+ recordBuckets: require_sleep.method(zod.z.object({ buckets: zod.z.array(zod.z.object({
14090
+ key: zod.z.string(),
14091
+ subject: DeviceHistorySubjectSchema,
14092
+ scope: zod.z.string().default(""),
14093
+ bucketStart: zod.z.number(),
14094
+ samples: zod.z.number().int().positive(),
14095
+ sum: zod.z.number(),
14096
+ min: zod.z.number(),
14097
+ max: zod.z.number(),
14098
+ last: zod.z.number().nullable(),
14099
+ lastTs: zod.z.number().nullable()
14100
+ })).max(5e3) }), IngestResultSchema, {
14101
+ kind: "mutation",
14102
+ auth: "admin"
14103
+ }),
14104
+ /** Per-device opt-out. This addon IS the authority: the switch stores nothing elsewhere (D62). */
14105
+ getDeviceEnabled: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int() }), zod.z.object({ enabled: zod.z.boolean() })),
14106
+ setDeviceEnabled: require_sleep.method(zod.z.object({
14107
+ deviceId: zod.z.number().int(),
14108
+ enabled: zod.z.boolean()
14109
+ }), zod.z.object({ enabled: zod.z.boolean() }), {
14110
+ kind: "mutation",
14111
+ auth: "admin"
14112
+ }),
14113
+ getStatus: require_sleep.method(zod.z.object({}), DeviceHistoryStatusSchema, { auth: "admin" })
14114
+ }
14115
+ };
14116
+ //#endregion
13880
14117
  //#region src/capabilities/device-credentials.cap.ts
13881
14118
  var DeviceCaStatusSchema = zod.z.discriminatedUnion("state", [
13882
14119
  zod.z.object({ state: zod.z.literal("absent") }),
@@ -14077,6 +14314,16 @@ var nodeCertificatesCapability = {
14077
14314
  kind: "mutation",
14078
14315
  auth: "admin"
14079
14316
  }),
14317
+ /**
14318
+ * The hub unpairs this node (D857): it erases its mesh material, goes back to the
14319
+ * unpaired state and advertises itself again. The call is answered first; the wire
14320
+ * goes down right after, so the hub revokes the node's certificates BEFORE this
14321
+ * connection drops, never after.
14322
+ */
14323
+ forgetHub: require_sleep.method(zod.z.object({}), zod.z.object({ ok: zod.z.literal(true) }), {
14324
+ kind: "mutation",
14325
+ auth: "admin"
14326
+ }),
14080
14327
  /** Install the hub-signed HTTPS leaf and hot-swap the serving context (no restart). */
14081
14328
  installHttpsCertificate: require_sleep.method(zod.z.object({
14082
14329
  certPem: zod.z.string().min(1),
@@ -38173,6 +38420,23 @@ var HUB_MDNS_TXT_KEYS = {
38173
38420
  url: "url"
38174
38421
  };
38175
38422
  var HUB_MDNS_ROLE = "hub";
38423
+ /**
38424
+ * How a hub finds agents to pair (D857): every agent publishes ONE mDNS service for its API
38425
+ * port, paired or not. The TXT record carries only public facts. Finding an agent grants
38426
+ * nothing: pairing still needs the six-digit code confirmed on both screens.
38427
+ */
38428
+ var AGENT_MDNS_SERVICE_TYPE = "camstack-agent";
38429
+ /** The fully-qualified name a hub browses for. */
38430
+ var AGENT_MDNS_QUERY_NAME = "_camstack-agent._tcp.local";
38431
+ /** The TXT keys of the agent advertisement. Every value is public. */
38432
+ var AGENT_MDNS_TXT_KEYS = {
38433
+ nodeId: "nodeId",
38434
+ version: "version",
38435
+ /** `unpaired` | `pending` | `paired`. */
38436
+ state: "state",
38437
+ /** First 16 hex chars of the SHA-256 of the agent's mesh leaf; present once paired. */
38438
+ keyFingerprint: "fp"
38439
+ };
38176
38440
  //#endregion
38177
38441
  //#region src/capabilities/ai-cap-timeouts.ts
38178
38442
  /**
@@ -77465,6 +77729,66 @@ var METHOD_ACCESS_MAP = Object.freeze({
77465
77729
  addonId: null,
77466
77730
  access: "create"
77467
77731
  },
77732
+ "deviceHistory.declareSeries": {
77733
+ capName: "device-history",
77734
+ capScope: "system",
77735
+ addonId: null,
77736
+ access: "create"
77737
+ },
77738
+ "deviceHistory.getDeviceEnabled": {
77739
+ capName: "device-history",
77740
+ capScope: "system",
77741
+ addonId: null,
77742
+ access: "view"
77743
+ },
77744
+ "deviceHistory.getStatus": {
77745
+ capName: "device-history",
77746
+ capScope: "system",
77747
+ addonId: null,
77748
+ access: "view"
77749
+ },
77750
+ "deviceHistory.listNodeSeries": {
77751
+ capName: "device-history",
77752
+ capScope: "system",
77753
+ addonId: null,
77754
+ access: "view"
77755
+ },
77756
+ "deviceHistory.listSeries": {
77757
+ capName: "device-history",
77758
+ capScope: "system",
77759
+ addonId: null,
77760
+ access: "view"
77761
+ },
77762
+ "deviceHistory.query": {
77763
+ capName: "device-history",
77764
+ capScope: "system",
77765
+ addonId: null,
77766
+ access: "view"
77767
+ },
77768
+ "deviceHistory.queryNode": {
77769
+ capName: "device-history",
77770
+ capScope: "system",
77771
+ addonId: null,
77772
+ access: "view"
77773
+ },
77774
+ "deviceHistory.record": {
77775
+ capName: "device-history",
77776
+ capScope: "system",
77777
+ addonId: null,
77778
+ access: "create"
77779
+ },
77780
+ "deviceHistory.recordBuckets": {
77781
+ capName: "device-history",
77782
+ capScope: "system",
77783
+ addonId: null,
77784
+ access: "create"
77785
+ },
77786
+ "deviceHistory.setDeviceEnabled": {
77787
+ capName: "device-history",
77788
+ capScope: "system",
77789
+ addonId: null,
77790
+ access: "create"
77791
+ },
77468
77792
  "deviceManager.addLocation": {
77469
77793
  capName: "device-manager",
77470
77794
  capScope: "system",
@@ -79529,6 +79853,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
79529
79853
  addonId: null,
79530
79854
  access: "create"
79531
79855
  },
79856
+ "nodeCertificates.forgetHub": {
79857
+ capName: "node-certificates",
79858
+ capScope: "system",
79859
+ addonId: null,
79860
+ access: "delete"
79861
+ },
79532
79862
  "nodeCertificates.getCertificates": {
79533
79863
  capName: "node-certificates",
79534
79864
  capScope: "system",
@@ -83846,6 +84176,7 @@ var KNOWN_CAP_NAMES = [
83846
84176
  "device-discovery",
83847
84177
  "device-export",
83848
84178
  "device-extension",
84179
+ "device-history",
83849
84180
  "device-manager",
83850
84181
  "device-ops",
83851
84182
  "device-provider",
@@ -84084,6 +84415,7 @@ var SYSTEM_CAP_NAMES = [
84084
84415
  "device-adoption",
84085
84416
  "device-credentials",
84086
84417
  "device-export",
84418
+ "device-history",
84087
84419
  "device-manager",
84088
84420
  "device-provider",
84089
84421
  "device-state",
@@ -86114,6 +86446,7 @@ var CAPABILITY_NAMES = {
86114
86446
  deviceDiscovery: "device-discovery",
86115
86447
  deviceExport: "device-export",
86116
86448
  deviceExtension: "device-extension",
86449
+ deviceHistory: "device-history",
86117
86450
  deviceManager: "device-manager",
86118
86451
  deviceOps: "device-ops",
86119
86452
  deviceProvider: "device-provider",
@@ -86451,6 +86784,10 @@ var CAPABILITY_ROUTER_KEYS = [
86451
86784
  key: "deviceExtension",
86452
86785
  name: "device-extension"
86453
86786
  },
86787
+ {
86788
+ key: "deviceHistory",
86789
+ name: "device-history"
86790
+ },
86454
86791
  {
86455
86792
  key: "deviceManager",
86456
86793
  name: "device-manager"
@@ -86988,6 +87325,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
86988
87325
  deviceDiscoveryCapability,
86989
87326
  deviceExportCapability,
86990
87327
  deviceExtensionCapability,
87328
+ deviceHistoryCapability,
86991
87329
  deviceManagerCapability,
86992
87330
  require_sleep.deviceOpsCapability,
86993
87331
  deviceProviderCapability,
@@ -87573,6 +87911,26 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
87573
87911
  form: "single",
87574
87912
  optional: false
87575
87913
  }],
87914
+ "deviceHistory.getDeviceEnabled": [{
87915
+ name: "deviceId",
87916
+ form: "single",
87917
+ optional: false
87918
+ }],
87919
+ "deviceHistory.listSeries": [{
87920
+ name: "deviceId",
87921
+ form: "single",
87922
+ optional: false
87923
+ }],
87924
+ "deviceHistory.query": [{
87925
+ name: "deviceId",
87926
+ form: "single",
87927
+ optional: false
87928
+ }],
87929
+ "deviceHistory.setDeviceEnabled": [{
87930
+ name: "deviceId",
87931
+ form: "single",
87932
+ optional: false
87933
+ }],
87576
87934
  "deviceManager.adoptionRelease": [{
87577
87935
  name: "camDeviceId",
87578
87936
  form: "single",
@@ -89755,6 +90113,10 @@ var SYSTEM_SCOPE_DEVICE_METHODS = [
89755
90113
  "deviceExport.applyDeviceSettingsPatch",
89756
90114
  "deviceExport.getDeviceLiveContribution",
89757
90115
  "deviceExport.getDeviceSettingsContribution",
90116
+ "deviceHistory.getDeviceEnabled",
90117
+ "deviceHistory.listSeries",
90118
+ "deviceHistory.query",
90119
+ "deviceHistory.setDeviceEnabled",
89758
90120
  "deviceManager.adoptionRelease",
89759
90121
  "deviceManager.adoptionResync",
89760
90122
  "deviceManager.applyInitialMeta",
@@ -90289,6 +90651,107 @@ var RESTORED_CAP_NAMES = [
90289
90651
  "zones"
90290
90652
  ];
90291
90653
  //#endregion
90654
+ //#region src/history/bucket-accumulator.ts
90655
+ /** Width of a bucket the history store accepts. */
90656
+ var HISTORY_BUCKET_MS = 5 * 6e4;
90657
+ /** One hour of failed deliveries per series is retried; older buckets are dropped. */
90658
+ var MAX_REQUEUED_BUCKETS_PER_SERIES = 12;
90659
+ function bucketStartOf(at) {
90660
+ return Math.floor(at / HISTORY_BUCKET_MS) * HISTORY_BUCKET_MS;
90661
+ }
90662
+ function subjectId(subject) {
90663
+ return subject.kind === "device" ? `d:${subject.deviceId}` : `n:${subject.nodeId}`;
90664
+ }
90665
+ function seriesIdentity(key, subject, scope) {
90666
+ return `${key}\u0000${subjectId(subject)}\u0000${scope}`;
90667
+ }
90668
+ function toClosed(b) {
90669
+ return {
90670
+ key: b.key,
90671
+ subject: b.subject,
90672
+ scope: b.scope,
90673
+ bucketStart: b.bucketStart,
90674
+ samples: b.samples,
90675
+ sum: b.sum,
90676
+ min: b.min,
90677
+ max: b.max,
90678
+ last: b.last,
90679
+ lastTs: b.lastTs
90680
+ };
90681
+ }
90682
+ var BucketAccumulator = class {
90683
+ open = /* @__PURE__ */ new Map();
90684
+ requeued = /* @__PURE__ */ new Map();
90685
+ /** Add one observation. A non-finite value or timestamp is ignored (never counted as 0). */
90686
+ note(key, subject, scope, value, at) {
90687
+ if (!Number.isFinite(value) || !Number.isFinite(at)) return;
90688
+ const bucketStart = bucketStartOf(at);
90689
+ const id = `${seriesIdentity(key, subject, scope)}\u0000${bucketStart}`;
90690
+ const existing = this.open.get(id);
90691
+ if (existing === void 0) {
90692
+ this.open.set(id, {
90693
+ key,
90694
+ subject,
90695
+ scope,
90696
+ bucketStart,
90697
+ samples: 1,
90698
+ sum: value,
90699
+ min: value,
90700
+ max: value,
90701
+ last: value,
90702
+ lastTs: at
90703
+ });
90704
+ return;
90705
+ }
90706
+ existing.samples += 1;
90707
+ existing.sum += value;
90708
+ if (value < existing.min) existing.min = value;
90709
+ if (value > existing.max) existing.max = value;
90710
+ if (at >= existing.lastTs) {
90711
+ existing.last = value;
90712
+ existing.lastTs = at;
90713
+ }
90714
+ }
90715
+ /**
90716
+ * Buckets whose window ended at or before `now`, oldest first, requeued ones
90717
+ * included. They leave the accumulator: delivery is the caller's.
90718
+ */
90719
+ takeClosed(now) {
90720
+ const out = [];
90721
+ for (const [id, bucket] of this.open) if (bucket.bucketStart + 3e5 <= now) {
90722
+ out.push(toClosed(bucket));
90723
+ this.open.delete(id);
90724
+ }
90725
+ for (const list of this.requeued.values()) out.push(...list);
90726
+ this.requeued.clear();
90727
+ return out.toSorted((a, b) => a.bucketStart - b.bucketStart);
90728
+ }
90729
+ /**
90730
+ * Put undelivered buckets back for the next `takeClosed`. Keeps the newest
90731
+ * {@link MAX_REQUEUED_BUCKETS_PER_SERIES} per series and returns the buckets
90732
+ * it dropped (empty when none).
90733
+ */
90734
+ requeue(buckets) {
90735
+ const dropped = [];
90736
+ for (const bucket of buckets) {
90737
+ const id = seriesIdentity(bucket.key, bucket.subject, bucket.scope);
90738
+ const list = [...this.requeued.get(id) ?? [], bucket].toSorted((a, b) => a.bucketStart - b.bucketStart);
90739
+ while (list.length > 12) {
90740
+ const oldest = list.shift();
90741
+ if (oldest !== void 0) dropped.push(oldest);
90742
+ }
90743
+ this.requeued.set(id, list);
90744
+ }
90745
+ return dropped;
90746
+ }
90747
+ /** Buckets currently held (open plus requeued) — for a gauge in the producer's own status. */
90748
+ pendingBucketCount() {
90749
+ let n = this.open.size;
90750
+ for (const list of this.requeued.values()) n += list.length;
90751
+ return n;
90752
+ }
90753
+ };
90754
+ //#endregion
90292
90755
  //#region src/generated/history-catalog.ts
90293
90756
  /**
90294
90757
  * Cap history catalog, projected from every `*.cap.ts` `history` block.
@@ -90878,6 +91341,14 @@ function createSystemProxy(api) {
90878
91341
  exposeDevice: (input) => dispatch("deviceExport", "exposeDevice", "mutation", input),
90879
91342
  unexposeDevice: (input) => dispatch("deviceExport", "unexposeDevice", "mutation", input)
90880
91343
  },
91344
+ deviceHistory: {
91345
+ listNodeSeries: (input) => dispatch("deviceHistory", "listNodeSeries", "query", input),
91346
+ queryNode: (input) => dispatch("deviceHistory", "queryNode", "query", input),
91347
+ declareSeries: (input) => dispatch("deviceHistory", "declareSeries", "mutation", input),
91348
+ record: (input) => dispatch("deviceHistory", "record", "mutation", input),
91349
+ recordBuckets: (input) => dispatch("deviceHistory", "recordBuckets", "mutation", input),
91350
+ getStatus: (input) => dispatch("deviceHistory", "getStatus", "query", input)
91351
+ },
90881
91352
  deviceManager: {
90882
91353
  allocateDeviceId: (input) => dispatch("deviceManager", "allocateDeviceId", "mutation", input),
90883
91354
  migrateDevice: (input) => dispatch("deviceManager", "migrateDevice", "mutation", input),
@@ -99917,6 +100388,9 @@ function enumerateInferenceDevices(hw) {
99917
100388
  //#endregion
99918
100389
  exports.ACCESSORY_LABEL = ACCESSORY_LABEL;
99919
100390
  exports.ACCESS_ROLES = ACCESS_ROLES;
100391
+ exports.AGENT_MDNS_QUERY_NAME = AGENT_MDNS_QUERY_NAME;
100392
+ exports.AGENT_MDNS_SERVICE_TYPE = AGENT_MDNS_SERVICE_TYPE;
100393
+ exports.AGENT_MDNS_TXT_KEYS = AGENT_MDNS_TXT_KEYS;
99920
100394
  exports.ALEXA_EGRESS_PROFILE = ALEXA_EGRESS_PROFILE;
99921
100395
  exports.ALL_CAPABILITY_DEFINITIONS = ALL_CAPABILITY_DEFINITIONS;
99922
100396
  exports.APPEARANCE_BIN_COUNT = APPEARANCE_BIN_COUNT;
@@ -100068,6 +100542,7 @@ exports.BrokerSubscribeInputSchema = SubscribeInputSchema;
100068
100542
  exports.BrokerSubscribeResultSchema = SubscribeResultSchema;
100069
100543
  exports.BrokerTestConnectionResultSchema = TestConnectionResultSchema;
100070
100544
  exports.BrokerUnsubscribeInputSchema = UnsubscribeInputSchema;
100545
+ exports.BucketAccumulator = BucketAccumulator;
100071
100546
  exports.BulkRecordSchema = BulkRecordSchema;
100072
100547
  exports.CAMERA_SENSORS_NOT_LOADED = CAMERA_SENSORS_NOT_LOADED;
100073
100548
  exports.CAMERA_SENSORS_SETTINGS_KEY = CAMERA_SENSORS_SETTINGS_KEY;
@@ -100397,6 +100872,11 @@ exports.DeviceExportExposeInputSchema = ExposeInputSchema;
100397
100872
  exports.DeviceExportStatusSchema = DeviceExportStatusSchema;
100398
100873
  exports.DeviceExportUnexposeInputSchema = UnexposeInputSchema;
100399
100874
  exports.DeviceFeature = require_err_msg.DeviceFeature;
100875
+ exports.DeviceHistoryKindSchema = DeviceHistoryKindSchema;
100876
+ exports.DeviceHistoryRefusalReasonSchema = DeviceHistoryRefusalReasonSchema;
100877
+ exports.DeviceHistoryResolutionSchema = DeviceHistoryResolutionSchema;
100878
+ exports.DeviceHistoryStatusSchema = DeviceHistoryStatusSchema;
100879
+ exports.DeviceHistorySubjectSchema = DeviceHistorySubjectSchema;
100400
100880
  exports.DeviceInfoSchema = DeviceInfoSchema;
100401
100881
  exports.DeviceModelChoiceSchema = DeviceModelChoiceSchema;
100402
100882
  exports.DeviceNetworkStatsSchema = DeviceNetworkStatsSchema;
@@ -100558,6 +101038,7 @@ exports.HAP_AUDIO_VBV_KBITS = HAP_AUDIO_VBV_KBITS;
100558
101038
  exports.HAP_KEYFRAME_INTERVAL_SEC = HAP_KEYFRAME_INTERVAL_SEC;
100559
101039
  exports.HF_BASE_URL = require_sleep.HF_BASE_URL;
100560
101040
  exports.HF_REPO = require_sleep.HF_REPO;
101041
+ exports.HISTORY_BUCKET_MS = HISTORY_BUCKET_MS;
100561
101042
  exports.HISTORY_CATALOG = HISTORY_CATALOG;
100562
101043
  exports.HISTORY_DEFAULT_MIN_INTERVAL_MS = require_sleep.HISTORY_DEFAULT_MIN_INTERVAL_MS;
100563
101044
  exports.HISTORY_NONE_CAPS = HISTORY_NONE_CAPS;
@@ -100732,6 +101213,7 @@ exports.MAX_EXPRESSION_EVAL_STEPS = require_composition.MAX_EXPRESSION_EVAL_STEP
100732
101213
  exports.MAX_EXPRESSION_SOURCE_LENGTH = require_composition.MAX_EXPRESSION_SOURCE_LENGTH;
100733
101214
  exports.MAX_KEYS = MAX_KEYS;
100734
101215
  exports.MAX_REASONS_PER_KEY = MAX_REASONS_PER_KEY;
101216
+ exports.MAX_REQUEUED_BUCKETS_PER_SERIES = MAX_REQUEUED_BUCKETS_PER_SERIES;
100735
101217
  exports.MAX_SENSOR_TRIGGER_DEVICES = MAX_SENSOR_TRIGGER_DEVICES;
100736
101218
  exports.MAX_TRACK_DEBUG_NOTE_LEN = MAX_TRACK_DEBUG_NOTE_LEN;
100737
101219
  exports.MAX_TRACK_PURITIES = MAX_TRACK_PURITIES;
@@ -101734,6 +102216,7 @@ exports.binaryCapability = binaryCapability;
101734
102216
  exports.bindAddonActions = bindAddonActions;
101735
102217
  exports.brightnessCapability = brightnessCapability;
101736
102218
  exports.brokerCapability = brokerCapability;
102219
+ exports.bucketStartOf = bucketStartOf;
101737
102220
  exports.buildAddonRouteProvider = buildAddonRouteProvider;
101738
102221
  exports.buildAudioArgs = require_composition.buildAudioArgs;
101739
102222
  exports.buildBenchStepTree = buildBenchStepTree;
@@ -101901,6 +102384,7 @@ exports.deviceDiscoveryCapability = deviceDiscoveryCapability;
101901
102384
  exports.deviceDoFamily = deviceDoFamily;
101902
102385
  exports.deviceExportCapability = deviceExportCapability;
101903
102386
  exports.deviceExtensionCapability = deviceExtensionCapability;
102387
+ exports.deviceHistoryCapability = deviceHistoryCapability;
101904
102388
  exports.deviceManagerCapability = deviceManagerCapability;
101905
102389
  exports.deviceMatchesProfile = deviceMatchesProfile;
101906
102390
  exports.deviceOpsCapability = require_sleep.deviceOpsCapability;