@camstack/system 1.2.484 → 1.2.486

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 (89) hide show
  1. package/dist/{agent-https-DttPIFry.mjs → agent-https-C4PLR74Z.mjs} +1 -1
  2. package/dist/{agent-https-CW8aLMDL.js → agent-https-DP-Ds2hU.js} +1 -1
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/autotrack/index.js +1 -1
  10. package/dist/builtins/autotrack/index.mjs +1 -1
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  13. package/dist/builtins/calendars/calendars.addon.js +1 -1
  14. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  15. package/dist/builtins/camera-grid/index.js +1 -1
  16. package/dist/builtins/camera-grid/index.mjs +1 -1
  17. package/dist/builtins/composer/composer.addon.js +69 -15
  18. package/dist/builtins/composer/composer.addon.mjs +69 -15
  19. package/dist/builtins/composer/composition-runtime.d.ts +2 -0
  20. package/dist/builtins/console-logging/index.js +1 -1
  21. package/dist/builtins/console-logging/index.mjs +1 -1
  22. package/dist/builtins/core-blocks/composition-sources.d.ts +3 -1
  23. package/dist/builtins/core-blocks/core-blocks.addon.js +53 -28
  24. package/dist/builtins/core-blocks/core-blocks.addon.mjs +53 -28
  25. package/dist/builtins/device-history/device-history.addon.d.ts +2 -0
  26. package/dist/builtins/device-history/device-history.addon.js +248 -4
  27. package/dist/builtins/device-history/device-history.addon.mjs +248 -4
  28. package/dist/builtins/device-history/query/history-query.d.ts +5 -1
  29. package/dist/builtins/device-history/runtime/history-runtime.d.ts +18 -1
  30. package/dist/builtins/device-history/store/history-schema.d.ts +2 -0
  31. package/dist/builtins/device-history/store/history-store.d.ts +11 -0
  32. package/dist/builtins/device-history/store/series-scale.d.ts +37 -0
  33. package/dist/builtins/device-manager/device-manager.addon.js +3 -2
  34. package/dist/builtins/device-manager/device-manager.addon.mjs +3 -2
  35. package/dist/builtins/hub-forwarder/index.js +1 -1
  36. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  37. package/dist/builtins/keychain/index.js +3 -3
  38. package/dist/builtins/keychain/index.mjs +3 -3
  39. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  40. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  41. package/dist/builtins/local-auth/local-auth.addon.js +2 -2
  42. package/dist/builtins/local-auth/local-auth.addon.mjs +2 -2
  43. package/dist/builtins/local-network/local-network.addon.js +2 -2
  44. package/dist/builtins/local-network/local-network.addon.mjs +2 -2
  45. package/dist/builtins/loki-logging/index.js +1 -1
  46. package/dist/builtins/loki-logging/index.mjs +1 -1
  47. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  48. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  49. package/dist/builtins/platform-probe/index.js +1 -1
  50. package/dist/builtins/platform-probe/index.mjs +1 -1
  51. package/dist/builtins/record-entries/index.js +1 -1
  52. package/dist/builtins/record-entries/index.mjs +1 -1
  53. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  54. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  55. package/dist/builtins/snapshot/index.js +1 -1
  56. package/dist/builtins/snapshot/index.mjs +1 -1
  57. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  58. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  59. package/dist/builtins/sqlite-storage/index.js +1 -1
  60. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  61. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
  62. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
  63. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  64. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  65. package/dist/builtins/system-config/system-config.addon.js +3 -3
  66. package/dist/builtins/system-config/system-config.addon.mjs +3 -3
  67. package/dist/builtins/winston-logging/index.js +1 -1
  68. package/dist/builtins/winston-logging/index.mjs +1 -1
  69. package/dist/{composition-sources-DqRPHdtw.js → composition-sources-CN8K2Q91.js} +4 -2
  70. package/dist/{composition-sources-DEsDonpg.mjs → composition-sources-IxiUkrz1.mjs} +4 -2
  71. package/dist/{constants-71vf77HY.js → constants-1S7BBGMb.js} +1 -1
  72. package/dist/{constants-B3FDrBIq.mjs → constants-DLyGFHZ2.mjs} +1 -1
  73. package/dist/{device-ca-bhz04X_N.mjs → device-ca-Cbf7_R9t.mjs} +1 -1
  74. package/dist/{device-ca-BDPYXhkH.js → device-ca-DoOFG0nm.js} +1 -1
  75. package/dist/{dist-k--OhKkO.js → dist-D-d_UWL4.js} +1375 -537
  76. package/dist/{dist-BT_THdzJ.mjs → dist-HBue971z.mjs} +1364 -538
  77. package/dist/file-host-worker.js +1 -1
  78. package/dist/file-host-worker.mjs +1 -1
  79. package/dist/{hub-mdns-ciao-DDgdvcQw.mjs → hub-mdns-ciao-CuEWb6gB.mjs} +2 -2
  80. package/dist/{hub-mdns-ciao-CGXfhvTk.js → hub-mdns-ciao-dcacrwqd.js} +2 -2
  81. package/dist/index.js +7 -7
  82. package/dist/index.mjs +7 -7
  83. package/dist/{restore-archive-z0XT2g29.mjs → restore-archive-BGBRadFJ.mjs} +1 -1
  84. package/dist/{restore-archive-B-GiglWt.js → restore-archive-DHdXrKwG.js} +1 -1
  85. package/dist/{retention-DFkmhjeH.mjs → retention-8uqDWll8.mjs} +1 -1
  86. package/dist/{retention-DR0_xRTy.js → retention-ChgrkTfz.js} +1 -1
  87. package/dist/{wal-maintenance-C4cma95b.js → wal-maintenance-BOT7wWvH.js} +0 -0
  88. package/dist/{wal-maintenance-CVDrL_BD.mjs → wal-maintenance-DubGwqz2.mjs} +0 -0
  89. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-k--OhKkO.js");
3
+ const require_dist = require("../../dist-D-d_UWL4.js");
4
4
  const require_read_all_rows$1 = require("../../read-all-rows-CVoO1ONM.js");
5
- const require_composition_sources = require("../../composition-sources-DqRPHdtw.js");
5
+ const require_composition_sources = require("../../composition-sources-CN8K2Q91.js");
6
6
  const require_prefix_range = require("../../prefix-range-D_KueXbQ.js");
7
7
  const require_prefix_walk = require("../../prefix-walk-Dv6MLZlM.js");
8
8
  let zod = require("zod");
@@ -1933,6 +1933,16 @@ function refuseIfChanged(deps, blockId, expectedUpdatedAt) {
1933
1933
  function existingTarget(composition) {
1934
1934
  return composition.target.kind === "existing" ? composition.target.device : null;
1935
1935
  }
1936
+ function helperMemberDeviceIds(composition, sources) {
1937
+ const helper = composition.helper;
1938
+ if (helper === void 0) return [];
1939
+ const ids = [];
1940
+ for (const member of helper.members) {
1941
+ const found = sources.get(require_dist.compositionSourceKey(member.source));
1942
+ if (found?.kind === "found") ids.push(found.deviceId);
1943
+ }
1944
+ return ids;
1945
+ }
1936
1946
  async function validateAgainstStore(deps, composition, blockId) {
1937
1947
  if (!deps.store.isLoaded()) throw new Error(`${NOT_LOADED_REASON} — a composition cannot be judged against the stored ones until it has; retry shortly`);
1938
1948
  const self = blockId === null ? null : require_dist.composedDeviceRef(blockId);
@@ -1942,29 +1952,37 @@ async function validateAgainstStore(deps, composition, blockId) {
1942
1952
  const blocks = deps.store.list();
1943
1953
  const read = await deps.describe(refs, [...target === null ? [] : [target], ...storedCustomizationTargets(blocks)]);
1944
1954
  const stored = storedPeers(blocks, read.targets, deps.lookupCap, blockId);
1945
- return require_dist.validateComposition(composition, {
1946
- lookupCap: deps.lookupCap,
1947
- sources: read.sources,
1948
- self,
1949
- blockId,
1950
- target: target === null ? null : read.targets.get(require_dist.compositionSourceKey(target)) ?? {
1951
- kind: "unreadable",
1952
- error: "it was not read"
1953
- },
1954
- peers: stored.peers,
1955
- unreadTargets: stored.unread
1956
- });
1955
+ return {
1956
+ validation: require_dist.validateComposition(composition, {
1957
+ lookupCap: deps.lookupCap,
1958
+ sources: read.sources,
1959
+ self,
1960
+ blockId,
1961
+ target: target === null ? null : read.targets.get(require_dist.compositionSourceKey(target)) ?? {
1962
+ kind: "unreadable",
1963
+ error: "it was not read"
1964
+ },
1965
+ peers: stored.peers,
1966
+ unreadTargets: stored.unread
1967
+ }),
1968
+ memberDeviceIds: helperMemberDeviceIds(composition, read.sources)
1969
+ };
1957
1970
  }
1958
1971
  function describeProblems(validation) {
1959
1972
  return validation.problems.map((p) => `${p.path} [${p.code}]: ${p.message}`);
1960
1973
  }
1961
- function refuse(deps, subject, validation) {
1974
+ function refuse(deps, subject, validation, memberDeviceIds) {
1962
1975
  const problems = describeProblems(validation);
1963
- deps.logger.warn("composition save refused", { meta: {
1976
+ const meta = {
1964
1977
  name: subject.name,
1965
1978
  blockId: subject.blockId,
1966
1979
  problems
1967
- } });
1980
+ };
1981
+ if (memberDeviceIds.length === 0) deps.logger.warn("composition save refused", { meta });
1982
+ else for (const deviceId of memberDeviceIds) deps.logger.warn("composition save refused", {
1983
+ tags: { deviceId },
1984
+ meta
1985
+ });
1968
1986
  throw new Error(`core-blocks: the composition "${subject.name}" was refused — ${problems.join("; ")}`);
1969
1987
  }
1970
1988
  async function readStates(deps, blockIds) {
@@ -2109,11 +2127,11 @@ function buildCompositionApi(deps) {
2109
2127
  return {
2110
2128
  createComposition: ({ block, caller }) => serialized(async () => {
2111
2129
  const name = await nameOnCreate(deps, block);
2112
- const validation = await validateAgainstStore(deps, block.composition, null);
2113
- if (!validation.ok && require_dist.problemsBlockSave(validation.problems)) refuse(deps, {
2130
+ const judged = await validateAgainstStore(deps, block.composition, null);
2131
+ if (!judged.validation.ok && require_dist.problemsBlockSave(judged.validation.problems)) refuse(deps, {
2114
2132
  name,
2115
2133
  blockId: null
2116
- }, validation);
2134
+ }, judged.validation, judged.memberDeviceIds);
2117
2135
  const created = await deps.store.createComposition({
2118
2136
  ...block,
2119
2137
  name
@@ -2130,11 +2148,11 @@ function buildCompositionApi(deps) {
2130
2148
  refuseIfChanged(deps, blockId, expectedUpdatedAt);
2131
2149
  await refuseRetarget(deps, blockId, block);
2132
2150
  if (block.composition !== void 0) {
2133
- const validation = await validateAgainstStore(deps, block.composition, blockId);
2134
- if (!validation.ok && require_dist.problemsBlockSave(validation.problems)) refuse(deps, {
2151
+ const judged = await validateAgainstStore(deps, block.composition, blockId);
2152
+ if (!judged.validation.ok && require_dist.problemsBlockSave(judged.validation.problems)) refuse(deps, {
2135
2153
  name: block.name ?? blockId,
2136
2154
  blockId
2137
- }, validation);
2155
+ }, judged.validation, judged.memberDeviceIds);
2138
2156
  }
2139
2157
  const updated = await deps.store.updateComposition(blockId, block);
2140
2158
  deps.logger.info("composition updated", { meta: {
@@ -2212,7 +2230,7 @@ function buildCompositionApi(deps) {
2212
2230
  }
2213
2231
  };
2214
2232
  },
2215
- validateComposition: async ({ composition, blockId }) => validateAgainstStore(deps, composition, blockId ?? null),
2233
+ validateComposition: async ({ composition, blockId }) => (await validateAgainstStore(deps, composition, blockId ?? null)).validation,
2216
2234
  listComposableCaps: async ({ deviceType }) => ({ caps: [...require_dist.describeComposableCaps(deps.capabilityDefinitions, deviceType)] })
2217
2235
  };
2218
2236
  }
@@ -2637,10 +2655,17 @@ var CoreBlocksAddon = class extends require_dist.BaseAddon {
2637
2655
  return buildCompositionApi({
2638
2656
  store: this.requireStore(),
2639
2657
  describe: (sources, targets) => require_composition_sources.describeCompositionSources({
2640
- listByAddon: async (addonId) => requireApi().deviceManager.listAll.query({
2641
- addonId,
2642
- projection: "slim"
2643
- }),
2658
+ listByAddon: async (addonId) => {
2659
+ return (await requireApi().deviceManager.listAll.query({
2660
+ addonId,
2661
+ projection: "slim"
2662
+ })).map((row) => ({
2663
+ id: row.id,
2664
+ stableId: row.stableId,
2665
+ type: row.type,
2666
+ ...row.sourceInfo?.quantity === void 0 ? {} : { quantity: row.sourceInfo.quantity }
2667
+ }));
2668
+ },
2644
2669
  bindings: async (deviceId) => (await requireApi().deviceManager.getBindings.query({ deviceId })).entries
2645
2670
  }, sources, targets),
2646
2671
  lookupCap: require_dist.buildCapabilityLookup(require_dist.ALL_CAPABILITY_DEFINITIONS),
@@ -1,6 +1,6 @@
1
- import { $n as planComposition, Bt as coreBlockComposition, Ea as transformSecretWrite, Ht as coreBlockSourceKind, Ji as defineRecordKinds, Jr as compositionSourceKey, Kn as normalizeCoreBlockWrite, N as DeclaredDevices, Ot as buildCapabilityLookup, Pr as validateComposition, Rt as compositionSourceRefs, Sa as secretTreeFromSchema, Ui as collectSecretMarkerRefs, Ur as composedDeviceRef, Ut as coreBlocksCapability, Vt as coreBlockIdFromAddonId, Zt as describeComposableCaps, aa as isSecretMarker, dr as resolveCoreBlockFiles, ea as hydrateSchema, f as COMPOSER_RUNNER_ID, m as CORE_BLOCK_ADDON_PREFIX, ni as BaseAddon, p as CORE_BLOCKS_ADDON_ID, r as ALL_CAPABILITY_DEFINITIONS, sa as isSecretRefOwnedBy, sr as replacedFieldKeys, ti as errMsg, tr as problemsBlockSave, ur as resolveCoreBlockEntry, v as CoreBlockSchema, ya as resolveSecretMarkers, zt as coreBlockAddonId } from "../../dist-BT_THdzJ.mjs";
1
+ import { $n as planComposition, Bt as coreBlockComposition, Ht as coreBlockSourceKind, Ji as defineRecordKinds, Jr as compositionSourceKey, Kn as normalizeCoreBlockWrite, N as DeclaredDevices, Oa as transformSecretWrite, Ot as buildCapabilityLookup, Pr as validateComposition, Rt as compositionSourceRefs, Ui as collectSecretMarkerRefs, Ur as composedDeviceRef, Ut as coreBlocksCapability, Vt as coreBlockIdFromAddonId, Zt as describeComposableCaps, dr as resolveCoreBlockFiles, f as COMPOSER_RUNNER_ID, la as isSecretRefOwnedBy, m as CORE_BLOCK_ADDON_PREFIX, ni as BaseAddon, p as CORE_BLOCKS_ADDON_ID, r as ALL_CAPABILITY_DEFINITIONS, sa as isSecretMarker, sr as replacedFieldKeys, ta as hydrateSchema, ti as errMsg, tr as problemsBlockSave, ur as resolveCoreBlockEntry, v as CoreBlockSchema, wa as secretTreeFromSchema, xa as resolveSecretMarkers, zt as coreBlockAddonId } from "../../dist-HBue971z.mjs";
2
2
  import { t as require_read_all_rows } from "../../read-all-rows-TaPqdWNF.mjs";
3
- import { t as describeCompositionSources } from "../../composition-sources-DEsDonpg.mjs";
3
+ import { t as describeCompositionSources } from "../../composition-sources-IxiUkrz1.mjs";
4
4
  import { t as prefixRange } from "../../prefix-range-Dl0znowN.mjs";
5
5
  import { n as unwrapStoredValue, t as loadPrefixed } from "../../prefix-walk-DAOBSyH1.mjs";
6
6
  import { createRequire } from "node:module";
@@ -1928,6 +1928,16 @@ function refuseIfChanged(deps, blockId, expectedUpdatedAt) {
1928
1928
  function existingTarget(composition) {
1929
1929
  return composition.target.kind === "existing" ? composition.target.device : null;
1930
1930
  }
1931
+ function helperMemberDeviceIds(composition, sources) {
1932
+ const helper = composition.helper;
1933
+ if (helper === void 0) return [];
1934
+ const ids = [];
1935
+ for (const member of helper.members) {
1936
+ const found = sources.get(compositionSourceKey(member.source));
1937
+ if (found?.kind === "found") ids.push(found.deviceId);
1938
+ }
1939
+ return ids;
1940
+ }
1931
1941
  async function validateAgainstStore(deps, composition, blockId) {
1932
1942
  if (!deps.store.isLoaded()) throw new Error(`${NOT_LOADED_REASON} — a composition cannot be judged against the stored ones until it has; retry shortly`);
1933
1943
  const self = blockId === null ? null : composedDeviceRef(blockId);
@@ -1937,29 +1947,37 @@ async function validateAgainstStore(deps, composition, blockId) {
1937
1947
  const blocks = deps.store.list();
1938
1948
  const read = await deps.describe(refs, [...target === null ? [] : [target], ...storedCustomizationTargets(blocks)]);
1939
1949
  const stored = storedPeers(blocks, read.targets, deps.lookupCap, blockId);
1940
- return validateComposition(composition, {
1941
- lookupCap: deps.lookupCap,
1942
- sources: read.sources,
1943
- self,
1944
- blockId,
1945
- target: target === null ? null : read.targets.get(compositionSourceKey(target)) ?? {
1946
- kind: "unreadable",
1947
- error: "it was not read"
1948
- },
1949
- peers: stored.peers,
1950
- unreadTargets: stored.unread
1951
- });
1950
+ return {
1951
+ validation: validateComposition(composition, {
1952
+ lookupCap: deps.lookupCap,
1953
+ sources: read.sources,
1954
+ self,
1955
+ blockId,
1956
+ target: target === null ? null : read.targets.get(compositionSourceKey(target)) ?? {
1957
+ kind: "unreadable",
1958
+ error: "it was not read"
1959
+ },
1960
+ peers: stored.peers,
1961
+ unreadTargets: stored.unread
1962
+ }),
1963
+ memberDeviceIds: helperMemberDeviceIds(composition, read.sources)
1964
+ };
1952
1965
  }
1953
1966
  function describeProblems(validation) {
1954
1967
  return validation.problems.map((p) => `${p.path} [${p.code}]: ${p.message}`);
1955
1968
  }
1956
- function refuse(deps, subject, validation) {
1969
+ function refuse(deps, subject, validation, memberDeviceIds) {
1957
1970
  const problems = describeProblems(validation);
1958
- deps.logger.warn("composition save refused", { meta: {
1971
+ const meta = {
1959
1972
  name: subject.name,
1960
1973
  blockId: subject.blockId,
1961
1974
  problems
1962
- } });
1975
+ };
1976
+ if (memberDeviceIds.length === 0) deps.logger.warn("composition save refused", { meta });
1977
+ else for (const deviceId of memberDeviceIds) deps.logger.warn("composition save refused", {
1978
+ tags: { deviceId },
1979
+ meta
1980
+ });
1963
1981
  throw new Error(`core-blocks: the composition "${subject.name}" was refused — ${problems.join("; ")}`);
1964
1982
  }
1965
1983
  async function readStates(deps, blockIds) {
@@ -2104,11 +2122,11 @@ function buildCompositionApi(deps) {
2104
2122
  return {
2105
2123
  createComposition: ({ block, caller }) => serialized(async () => {
2106
2124
  const name = await nameOnCreate(deps, block);
2107
- const validation = await validateAgainstStore(deps, block.composition, null);
2108
- if (!validation.ok && problemsBlockSave(validation.problems)) refuse(deps, {
2125
+ const judged = await validateAgainstStore(deps, block.composition, null);
2126
+ if (!judged.validation.ok && problemsBlockSave(judged.validation.problems)) refuse(deps, {
2109
2127
  name,
2110
2128
  blockId: null
2111
- }, validation);
2129
+ }, judged.validation, judged.memberDeviceIds);
2112
2130
  const created = await deps.store.createComposition({
2113
2131
  ...block,
2114
2132
  name
@@ -2125,11 +2143,11 @@ function buildCompositionApi(deps) {
2125
2143
  refuseIfChanged(deps, blockId, expectedUpdatedAt);
2126
2144
  await refuseRetarget(deps, blockId, block);
2127
2145
  if (block.composition !== void 0) {
2128
- const validation = await validateAgainstStore(deps, block.composition, blockId);
2129
- if (!validation.ok && problemsBlockSave(validation.problems)) refuse(deps, {
2146
+ const judged = await validateAgainstStore(deps, block.composition, blockId);
2147
+ if (!judged.validation.ok && problemsBlockSave(judged.validation.problems)) refuse(deps, {
2130
2148
  name: block.name ?? blockId,
2131
2149
  blockId
2132
- }, validation);
2150
+ }, judged.validation, judged.memberDeviceIds);
2133
2151
  }
2134
2152
  const updated = await deps.store.updateComposition(blockId, block);
2135
2153
  deps.logger.info("composition updated", { meta: {
@@ -2207,7 +2225,7 @@ function buildCompositionApi(deps) {
2207
2225
  }
2208
2226
  };
2209
2227
  },
2210
- validateComposition: async ({ composition, blockId }) => validateAgainstStore(deps, composition, blockId ?? null),
2228
+ validateComposition: async ({ composition, blockId }) => (await validateAgainstStore(deps, composition, blockId ?? null)).validation,
2211
2229
  listComposableCaps: async ({ deviceType }) => ({ caps: [...describeComposableCaps(deps.capabilityDefinitions, deviceType)] })
2212
2230
  };
2213
2231
  }
@@ -2632,10 +2650,17 @@ var CoreBlocksAddon = class extends BaseAddon {
2632
2650
  return buildCompositionApi({
2633
2651
  store: this.requireStore(),
2634
2652
  describe: (sources, targets) => describeCompositionSources({
2635
- listByAddon: async (addonId) => requireApi().deviceManager.listAll.query({
2636
- addonId,
2637
- projection: "slim"
2638
- }),
2653
+ listByAddon: async (addonId) => {
2654
+ return (await requireApi().deviceManager.listAll.query({
2655
+ addonId,
2656
+ projection: "slim"
2657
+ })).map((row) => ({
2658
+ id: row.id,
2659
+ stableId: row.stableId,
2660
+ type: row.type,
2661
+ ...row.sourceInfo?.quantity === void 0 ? {} : { quantity: row.sourceInfo.quantity }
2662
+ }));
2663
+ },
2639
2664
  bindings: async (deviceId) => (await requireApi().deviceManager.getBindings.query({ deviceId })).entries
2640
2665
  }, sources, targets),
2641
2666
  lookupCap: buildCapabilityLookup(ALL_CAPABILITY_DEFINITIONS),
@@ -41,6 +41,8 @@ export declare class DeviceHistoryAddon extends BaseAddon<DeviceHistoryConfig> {
41
41
  /** Readiness of both provided caps, on the bare node this runner belongs to. */
42
42
  private emitStoreReadiness;
43
43
  private retention;
44
+ /** Unraid array counters still stored in GiB. A failed listing throws to the caller. */
45
+ private legacyScales;
44
46
  private requireRuntime;
45
47
  protected onInitialize(): Promise<ProviderRegistration[]>;
46
48
  override onHubReachable(): Promise<void>;
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-k--OhKkO.js");
7
- const require_retention = require("../../retention-DR0_xRTy.js");
6
+ const require_dist = require("../../dist-D-d_UWL4.js");
7
+ const require_retention = require("../../retention-ChgrkTfz.js");
8
8
  //#region src/builtins/device-history/capture/point-writer.ts
9
9
  var FLUSH_INTERVAL_MS = 1e4;
10
10
  var MAX_BUFFER_AGE_MS = 6e4;
@@ -451,6 +451,13 @@ var MAX_SCOPES = 50;
451
451
  /** The power-meter series the energy key is derived from, meter first. */
452
452
  var ENERGY_METER_KEY = require_retention.capSeriesKey("power-meter", "kwhTotal");
453
453
  var ENERGY_POWER_KEY = require_retention.capSeriesKey("power-meter", "watts");
454
+ /**
455
+ * Series whose quantity is chosen per device (D944). Weather is not here:
456
+ * each weather field has its own quantity on the capability, and the catalog
457
+ * unit is that base. A past weather point stored in another unit cannot be
458
+ * rescaled — the unit at sample time was not kept.
459
+ */
460
+ var DEVICE_QUANTITY_KEYS = new Set([require_retention.capSeriesKey("numeric-sensor", "value")]);
454
461
  function retentionMsOf(tier, policy) {
455
462
  if (tier === "raw") return policy.rawDays * require_retention.DAY_MS;
456
463
  if (tier === "5m") return policy.fiveMinuteDays * require_retention.DAY_MS;
@@ -717,12 +724,26 @@ var HistoryQuery = class {
717
724
  /** The metric's fixed unit, else the device's own (a `unitFrom` field), else unknown. */
718
725
  async unitOf(subject, series, fixed, slices) {
719
726
  if (fixed !== null || subject.kind !== "device" || !series.key.startsWith("cap:")) return fixed;
727
+ if (DEVICE_QUANTITY_KEYS.has(series.key)) {
728
+ const quantity = await this.quantityOf(subject.deviceId);
729
+ if (quantity === null || quantity === "device" || quantity === "none") return null;
730
+ return require_dist.historyUnitFor(quantity) ?? null;
731
+ }
720
732
  const spec = this.specOf(series.key);
721
733
  if (spec?.unitFrom === void 0) return null;
722
734
  const capName = series.key.slice(require_retention.CAP_KEY_PREFIX.length, series.key.indexOf("."));
723
735
  const slice = await this.sliceOf(subject.deviceId, capName, slices);
724
736
  return slice === null ? null : require_retention.readUnit(slice, spec.unitFrom);
725
737
  }
738
+ quantityOf(deviceId) {
739
+ const pending = this.quantities.get(deviceId);
740
+ if (pending !== void 0) return pending;
741
+ const read = this.deps.readDeviceQuantity;
742
+ const run = read === void 0 ? Promise.resolve(null) : read(deviceId).catch(() => null);
743
+ this.quantities.set(deviceId, run);
744
+ return run;
745
+ }
746
+ quantities = /* @__PURE__ */ new Map();
726
747
  specOf(key) {
727
748
  const body = key.slice(require_retention.CAP_KEY_PREFIX.length);
728
749
  const dot = body.indexOf(".");
@@ -882,6 +903,59 @@ var OrphanSeries = class {
882
903
  }
883
904
  };
884
905
  //#endregion
906
+ //#region src/builtins/device-history/store/series-scale.ts
907
+ /**
908
+ * One-shot rescales of series whose every stored point shared one factor
909
+ * (D944). A reading already in the base unit is at or above `legacyMax` and
910
+ * is left alone, so a second pass does not multiply it again.
911
+ *
912
+ * Rollup `sum` / `tw_sum` follow the bucket when min, max and last are still
913
+ * in the old unit. Judging the sum itself would skip a day's samples of a
914
+ * large gauge (many × GiB already exceeds the ceiling) and keep the raw
915
+ * points and the rollup in different units.
916
+ */
917
+ var GIB_TO_BYTES = 1024 ** 3;
918
+ var DAY_TO_SECONDS = 86400;
919
+ /** A homelab array in GiB is below 1 MiB; the same array in bytes is not. */
920
+ var INFORMATION_LEGACY_MAX = 1024 ** 2;
921
+ /** Desiccant life in days is below this; the same life in seconds is not. */
922
+ var DESICCANT_LEGACY_MAX_DAYS = 1e4;
923
+ var DESICCANT_SERIES_KEY = "cap:pet-feeder.desiccantLeftDays";
924
+ var DESICCANT_SCALE_ID = "pet-feeder-desiccant-days-to-seconds";
925
+ var UNRAID_INFORMATION_KEYS = [
926
+ "array_total",
927
+ "array_used",
928
+ "array_free"
929
+ ];
930
+ var NUMERIC_SENSOR_SERIES_KEY = "cap:numeric-sensor.value";
931
+ /** The stored number after one rescale, or the same number when it is already base. */
932
+ function scaledMagnitude(value, factor, legacyMax) {
933
+ if (!Number.isFinite(value) || value === 0) return value;
934
+ if (Math.abs(value) >= legacyMax) return value;
935
+ return value * factor;
936
+ }
937
+ function rollupIsLegacy(row, legacyMax) {
938
+ const samples = [
939
+ row.min,
940
+ row.max,
941
+ ...row.last === null ? [] : [row.last]
942
+ ];
943
+ if (samples.some((value) => !Number.isFinite(value))) return false;
944
+ if (samples.every((value) => value === 0)) return false;
945
+ return samples.every((value) => Math.abs(value) < legacyMax);
946
+ }
947
+ /** Unraid array capacity children. Other numeric sensors on the same key stay put. */
948
+ function unraidInformationScale(deviceId, entityKey) {
949
+ if (!UNRAID_INFORMATION_KEYS.includes(entityKey)) return null;
950
+ return {
951
+ seriesKey: NUMERIC_SENSOR_SERIES_KEY,
952
+ deviceId,
953
+ factor: GIB_TO_BYTES,
954
+ legacyMax: INFORMATION_LEGACY_MAX,
955
+ migrationId: `unraid-gib-to-bytes:${String(deviceId)}`
956
+ };
957
+ }
958
+ //#endregion
885
959
  //#region src/builtins/device-history/store/history-schema.ts
886
960
  var HISTORY_NAMESPACE = "device-history";
887
961
  var COLLECTIONS = {
@@ -892,7 +966,9 @@ var COLLECTIONS = {
892
966
  rollup1h: "rollup-1h",
893
967
  rollup1d: "rollup-1d",
894
968
  optOuts: "device-opt-outs",
895
- departures: "device-departures"
969
+ departures: "device-departures",
970
+ /** One row per applied base-unit rescale, in history.db so it travels with the points. */
971
+ appliedScales: "applied-scales"
896
972
  };
897
973
  function rollupCollectionOf(tier) {
898
974
  if (tier === "5m") return COLLECTIONS.rollup5m;
@@ -1145,6 +1221,21 @@ var HISTORY_DECLARATIONS = [
1145
1221
  columns: ["device_id"],
1146
1222
  unique: true
1147
1223
  }]
1224
+ },
1225
+ {
1226
+ namespace: HISTORY_NAMESPACE,
1227
+ collection: COLLECTIONS.appliedScales,
1228
+ database: "history",
1229
+ columns: [{
1230
+ name: "id",
1231
+ type: "TEXT",
1232
+ primaryKey: true,
1233
+ notNull: true
1234
+ }, {
1235
+ name: "applied_at",
1236
+ type: "INTEGER",
1237
+ notNull: true
1238
+ }]
1148
1239
  }
1149
1240
  ];
1150
1241
  //#endregion
@@ -1561,7 +1652,98 @@ var HistoryStore = class {
1561
1652
  filter: { where: { device_id: deviceId } }
1562
1653
  });
1563
1654
  }
1655
+ /** True when this rescale already finished. A missing row is "not yet". */
1656
+ async scaleApplied(id) {
1657
+ return (await this.door.query.query({
1658
+ namespace: HISTORY_NAMESPACE,
1659
+ collection: COLLECTIONS.appliedScales,
1660
+ filter: {
1661
+ where: { id },
1662
+ limit: 1
1663
+ }
1664
+ })).length > 0;
1665
+ }
1666
+ async markScale(id, at) {
1667
+ await this.door.upsertMany.mutate({
1668
+ namespace: HISTORY_NAMESPACE,
1669
+ collection: COLLECTIONS.appliedScales,
1670
+ conflict: ["id"],
1671
+ records: [{
1672
+ id,
1673
+ data: {
1674
+ id,
1675
+ applied_at: at
1676
+ }
1677
+ }]
1678
+ });
1679
+ }
1680
+ /**
1681
+ * Multiply one series' raw points and rollups that are still in the old
1682
+ * unit. Points already at or above `legacyMax` stay. Returns how many rows
1683
+ * changed.
1684
+ */
1685
+ async scaleSeries(seriesId, factor, legacyMax) {
1686
+ const raw = await this.scaleRaw(seriesId, factor, legacyMax);
1687
+ let rollups = 0;
1688
+ for (const tier of [
1689
+ "5m",
1690
+ "1h",
1691
+ "1d"
1692
+ ]) rollups += await this.scaleRollupTier(tier, seriesId, factor, legacyMax);
1693
+ return raw + rollups;
1694
+ }
1695
+ async scaleRaw(seriesId, factor, legacyMax) {
1696
+ let after = -1;
1697
+ let changed = 0;
1698
+ for (;;) {
1699
+ const rows = await this.readRaw(seriesId, after + 1, Number.MAX_SAFE_INTEGER, READ_PAGE, "asc");
1700
+ if (rows.length === 0) break;
1701
+ const next = rows.flatMap((row) => {
1702
+ const value = scaledMagnitude(row.value, factor, legacyMax);
1703
+ return value === row.value ? [] : [{
1704
+ seriesId,
1705
+ ts: row.ts,
1706
+ value
1707
+ }];
1708
+ });
1709
+ if (next.length > 0) {
1710
+ await this.upsertRaw(next);
1711
+ changed += next.length;
1712
+ }
1713
+ const last = rows[rows.length - 1];
1714
+ if (last === void 0 || rows.length < 5e3) break;
1715
+ after = last.ts;
1716
+ }
1717
+ return changed;
1718
+ }
1719
+ async scaleRollupTier(tier, seriesId, factor, legacyMax) {
1720
+ let after = -1;
1721
+ let changed = 0;
1722
+ for (;;) {
1723
+ const rows = await this.readRollups(tier, seriesId, after + 1, Number.MAX_SAFE_INTEGER, READ_PAGE, "asc");
1724
+ if (rows.length === 0) break;
1725
+ const next = rows.flatMap((row) => rollupIsLegacy(row, legacyMax) ? [scaleRollup(row, factor)] : []);
1726
+ if (next.length > 0) {
1727
+ await this.upsertRollups(tier, next);
1728
+ changed += next.length;
1729
+ }
1730
+ const last = rows[rows.length - 1];
1731
+ if (last === void 0 || rows.length < 5e3) break;
1732
+ after = last.bucketStart;
1733
+ }
1734
+ return changed;
1735
+ }
1564
1736
  };
1737
+ function scaleRollup(row, factor) {
1738
+ return {
1739
+ ...row,
1740
+ sum: row.sum * factor,
1741
+ min: row.min * factor,
1742
+ max: row.max * factor,
1743
+ last: row.last === null ? null : row.last * factor,
1744
+ twSum: row.twSum === null ? null : row.twSum * factor
1745
+ };
1746
+ }
1565
1747
  //#endregion
1566
1748
  //#region src/builtins/device-history/rollup/rollup-math.ts
1567
1749
  /**
@@ -2131,7 +2313,8 @@ var HistoryRuntime = class {
2131
2313
  retention: deps.retention,
2132
2314
  catalog,
2133
2315
  itemCatalog,
2134
- readCapSlice: deps.readCapSlice
2316
+ readCapSlice: deps.readCapSlice,
2317
+ readDeviceQuantity: deps.readDeviceQuantity
2135
2318
  });
2136
2319
  this.departures = new require_retention.DepartureTracker(this.store, this.registry, logger);
2137
2320
  this.registry.onRemove((series) => {
@@ -2167,10 +2350,53 @@ var HistoryRuntime = class {
2167
2350
  await this.capture.seed();
2168
2351
  }
2169
2352
  async finishBoot() {
2353
+ await this.applyLegacyScales();
2170
2354
  const series = await this.rollup.bootRecompute(this.deps.clock());
2171
2355
  this.deps.logger.info("device-history: recent rollup window rebuilt", { meta: { series } });
2172
2356
  await this.reconcile();
2173
2357
  }
2358
+ /**
2359
+ * PetKit desiccant was stored in days; the quantity base is seconds.
2360
+ * Unraid array counters were stored in GiB; the base is bytes. Both
2361
+ * factors are honest because the unit never varied inside the series.
2362
+ * Weather and numeric-sensor are not rescaled: the unit at sample time
2363
+ * was not stored, so a factor would invent a number (D393).
2364
+ */
2365
+ async applyLegacyScales() {
2366
+ await this.scaleMatching(DESICCANT_SERIES_KEY, void 0, DAY_TO_SECONDS, DESICCANT_LEGACY_MAX_DAYS, DESICCANT_SCALE_ID);
2367
+ const list = this.deps.listLegacyScales;
2368
+ if (list === void 0) return;
2369
+ let subjects;
2370
+ try {
2371
+ subjects = await list();
2372
+ } catch (err) {
2373
+ this.deps.logger.warn("device-history: legacy scale listing failed — stored points unchanged", { meta: { error: err instanceof Error ? err.message : String(err) } });
2374
+ return;
2375
+ }
2376
+ for (const subject of subjects) await this.scaleMatching(subject.seriesKey, subject.deviceId, subject.factor, subject.legacyMax, subject.migrationId);
2377
+ }
2378
+ async scaleMatching(key, deviceId, factor, legacyMax, migrationId) {
2379
+ if (await this.store.scaleApplied(migrationId)) return;
2380
+ const subject = deviceId === void 0 ? void 0 : require_retention.subjectIdOf({
2381
+ kind: "device",
2382
+ deviceId
2383
+ });
2384
+ const series = this.registry.all().filter((row) => {
2385
+ if (row.key !== key) return false;
2386
+ return subject === void 0 || row.subject === subject;
2387
+ });
2388
+ let changed = 0;
2389
+ for (const row of series) changed += await this.store.scaleSeries(row.id, factor, legacyMax);
2390
+ await this.store.markScale(migrationId, this.deps.clock());
2391
+ if (changed > 0) this.deps.logger.info("device-history: rescaled a series into its base unit", {
2392
+ ...deviceId === void 0 ? {} : { tags: { deviceId } },
2393
+ meta: {
2394
+ migrationId,
2395
+ key,
2396
+ changed
2397
+ }
2398
+ });
2399
+ }
2174
2400
  startTimers() {
2175
2401
  const every = (ms, run) => {
2176
2402
  const timer = setInterval(() => {
@@ -2448,6 +2674,19 @@ var DeviceHistoryAddon = class extends require_dist.BaseAddon {
2448
2674
  dayDays: positiveOr(c.dayDays, 0) > 0 ? c.dayDays : null
2449
2675
  };
2450
2676
  }
2677
+ /** Unraid array counters still stored in GiB. A failed listing throws to the caller. */
2678
+ async legacyScales() {
2679
+ const devices = await this.ctx.api.deviceManager.listAll.query({});
2680
+ const scales = [];
2681
+ for (const device of devices) {
2682
+ if (device.addonId !== "provider-unraid") continue;
2683
+ const entityKey = device.config["entityKey"];
2684
+ if (typeof entityKey !== "string") continue;
2685
+ const scale = unraidInformationScale(device.id, entityKey);
2686
+ if (scale !== null) scales.push(scale);
2687
+ }
2688
+ return scales;
2689
+ }
2451
2690
  requireRuntime() {
2452
2691
  if (this.runtime === null) throw new Error("device-history is starting — the history store is not open yet");
2453
2692
  return this.runtime;
@@ -2541,6 +2780,11 @@ var DeviceHistoryAddon = class extends require_dist.BaseAddon {
2541
2780
  deviceId,
2542
2781
  capName
2543
2782
  }),
2783
+ readDeviceQuantity: async (deviceId) => {
2784
+ const quantity = (await api.deviceManager.getDevice.query({ deviceId }))?.sourceInfo?.quantity;
2785
+ return require_dist.isReadingQuantity(quantity) ? quantity : null;
2786
+ },
2787
+ listLegacyScales: () => this.legacyScales(),
2544
2788
  retention: () => this.retention(),
2545
2789
  logger: this.ctx.logger,
2546
2790
  clock: () => Date.now()