@camstack/system 1.2.318 → 1.2.320

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 (76) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/autotrack/index.js +1 -1
  8. package/dist/builtins/autotrack/index.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +3 -77
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +3 -77
  11. package/dist/builtins/camera-grid/index.js +1 -1
  12. package/dist/builtins/camera-grid/index.mjs +1 -1
  13. package/dist/builtins/composer/composer-apply-waits.d.ts +21 -0
  14. package/dist/builtins/composer/composer-live-set.d.ts +22 -0
  15. package/dist/builtins/composer/composer-runtime-factory.d.ts +22 -0
  16. package/dist/builtins/composer/composer-seeds.d.ts +22 -0
  17. package/dist/builtins/composer/composer.addon.js +1905 -1809
  18. package/dist/builtins/composer/composer.addon.mjs +1905 -1809
  19. package/dist/builtins/composer/composer.d.ts +3 -15
  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/core-blocks.addon.js +2 -2
  23. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  24. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  25. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  26. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  27. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  28. package/dist/builtins/hub-forwarder/index.js +1 -1
  29. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  30. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  31. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  32. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  33. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  34. package/dist/builtins/local-network/local-network.addon.js +1 -1
  35. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  36. package/dist/builtins/loki-logging/index.js +1 -1
  37. package/dist/builtins/loki-logging/index.mjs +1 -1
  38. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  39. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  40. package/dist/builtins/platform-probe/index.js +1 -1
  41. package/dist/builtins/platform-probe/index.mjs +1 -1
  42. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  43. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  44. package/dist/builtins/snapshot/index.js +1 -1
  45. package/dist/builtins/snapshot/index.mjs +1 -1
  46. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  47. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  48. package/dist/builtins/sqlite-storage/retired-collections.d.ts +9 -0
  49. package/dist/builtins/sqlite-storage/shm-lock-canary.d.ts +60 -0
  50. package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +1 -0
  51. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  52. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  53. package/dist/builtins/sqlite-storage/wal-checkpoint-policy.d.ts +115 -34
  54. package/dist/builtins/sqlite-storage/wal-checkpointer.d.ts +6 -0
  55. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  56. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  57. package/dist/builtins/system-backup/system-backup.service.d.ts +2 -17
  58. package/dist/builtins/system-config/system-config.addon.js +1 -1
  59. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  60. package/dist/builtins/winston-logging/index.js +1 -1
  61. package/dist/builtins/winston-logging/index.mjs +1 -1
  62. package/dist/{composition-sources-BjRPhWtU.js → composition-sources-D4UkcIhl.js} +1 -1
  63. package/dist/{composition-sources-Cg2MP-qf.mjs → composition-sources-Due6vto8.mjs} +1 -1
  64. package/dist/{dist-Dcy0gVHu.js → dist-B0AvBZxe.js} +1 -174
  65. package/dist/{dist-CQ9vXSUh.mjs → dist-XfFiGX64.mjs} +1 -174
  66. package/dist/index.js +1 -1
  67. package/dist/index.mjs +1 -1
  68. package/dist/{retired-settings-keys-pzlrLUbA.js → retired-settings-keys-AmO1MZks.js} +1 -1
  69. package/dist/{retired-settings-keys-DNIbyfTR.mjs → retired-settings-keys-DXI77cc8.mjs} +1 -1
  70. package/dist/wal-checkpoint-policy-BahrTtyD.mjs +186 -0
  71. package/dist/wal-checkpoint-policy-d2z2qPqx.js +233 -0
  72. package/dist/wal-checkpoint-worker.js +63 -57
  73. package/dist/wal-checkpoint-worker.mjs +63 -57
  74. package/package.json +1 -1
  75. package/dist/wal-checkpoint-policy-CAcg63o-.mjs +0 -92
  76. package/dist/wal-checkpoint-policy-CCJZngds.js +0 -127
@@ -1,5 +1,4 @@
1
- import { a as isWorkerStop, o as parseWalIndexHeader, r as decideCheckpoint } from "./wal-checkpoint-policy-CAcg63o-.mjs";
2
- import { closeSync, openSync, readSync } from "node:fs";
1
+ import { c as workerStartObservation, o as isWorkerStop, s as nextCheckpointPlan } from "./wal-checkpoint-policy-BahrTtyD.mjs";
3
2
  import Database from "better-sqlite3";
4
3
  import { parentPort, workerData } from "node:worker_threads";
5
4
  //#region src/builtins/sqlite-storage/wal-checkpoint-worker.ts
@@ -20,9 +19,15 @@ import { parentPort, workerData } from "node:worker_threads";
20
19
  * writers and readers are never blocked by this thread. (The idle-gated
21
20
  * TRUNCATE in `wal-maintenance.ts` is a different job — reclaiming a
22
21
  * sidecar that outgrew its limit — and stays where it is.)
23
- * - The wal-index header is read as bytes (`-shm`, 100 bytes, no lock) so the
24
- * poll costs nothing; when it cannot be read the policy falls back to a
25
- * fixed interval, so nothing here can turn "unreadable" into "nothing to do".
22
+ * - It NEVER touches a SQLite file except through that connection — no
23
+ * `open`/`read`/`close` of the `.db`, `-wal` or `-shm`. Until 2026-09-29 it
24
+ * read the wal-index header out of `-shm` once a second, and each
25
+ * `closeSync` dropped every fcntl lock this PROCESS held on the file —
26
+ * SQLite's DMS lock included — which let the nightly backup's connection
27
+ * rebuild the `-shm` under the runner's mappings: the SIGBUS of D468,
28
+ * diagnosed in D679. `scripts/check-sqlite-files-not-opened-by-fs.ts`
29
+ * refuses the pattern. The schedule is derived from the checkpoints'
30
+ * own answers instead (`nextCheckpointPlan`).
26
31
  * - Every checkpoint is reported to the parent with its numbers. The parent
27
32
  * decides what is worth a line.
28
33
  *
@@ -46,31 +51,8 @@ function readCheckpointRow(value) {
46
51
  }
47
52
  function isWorkerData(value) {
48
53
  if (typeof value !== "object" || value === null) return false;
49
- if (!("dbPath" in value) || !("pollMs" in value)) return false;
50
- return typeof value.dbPath === "string" && typeof value.pollMs === "number";
51
- }
52
- /** The two wal-index counters, or nulls when the sidecar cannot be read. */
53
- function readWalIndex(shmPath) {
54
- let fd = null;
55
- try {
56
- fd = openSync(shmPath, "r");
57
- const buffer = Buffer.alloc(100);
58
- if (readSync(fd, buffer, 0, 100, 0) < 100) return {
59
- mxFrame: null,
60
- nBackfill: null
61
- };
62
- return parseWalIndexHeader(buffer) ?? {
63
- mxFrame: null,
64
- nBackfill: null
65
- };
66
- } catch {
67
- return {
68
- mxFrame: null,
69
- nBackfill: null
70
- };
71
- } finally {
72
- if (fd !== null) closeSync(fd);
73
- }
54
+ if (!("dbPath" in value) || !("pollMs" in value) || !("maxDelayMs" in value)) return false;
55
+ return typeof value.dbPath === "string" && typeof value.pollMs === "number" && typeof value.maxDelayMs === "number";
74
56
  }
75
57
  function post(message) {
76
58
  parentPort?.postMessage(message);
@@ -79,45 +61,69 @@ function run() {
79
61
  const port = parentPort;
80
62
  if (port === null) throw new Error("wal-checkpoint-worker: no parent port");
81
63
  if (!isWorkerData(workerData)) throw new Error("wal-checkpoint-worker: bad workerData");
82
- const { dbPath, pollMs } = workerData;
83
- const shmPath = `${dbPath}-shm`;
64
+ const { dbPath, pollMs, maxDelayMs } = workerData;
84
65
  const db = new Database(dbPath);
85
66
  db.pragma("cache_size = -2048");
86
67
  db.pragma("mmap_size = 0");
87
- let lastRunAt = Date.now();
68
+ let previous = workerStartObservation(Date.now());
69
+ let first = true;
88
70
  let timer = null;
89
71
  let stopping = false;
90
72
  const tick = () => {
91
73
  if (stopping) return;
92
- const decision = decideCheckpoint({
93
- ...readWalIndex(shmPath),
94
- sinceLastRunMs: Date.now() - lastRunAt
95
- });
96
- if (decision.run) {
97
- const startedAt = Date.now();
98
- let raw;
99
- try {
100
- raw = db.pragma("wal_checkpoint(PASSIVE)");
101
- } catch (err) {
102
- post({
103
- kind: "failed",
104
- error: err instanceof Error ? err.message : String(err)
105
- });
106
- raw = null;
107
- }
108
- lastRunAt = Date.now();
109
- const row = readCheckpointRow(raw);
110
- if (row !== null) post({
74
+ const startedAt = Date.now();
75
+ let raw = null;
76
+ try {
77
+ raw = db.pragma("wal_checkpoint(PASSIVE)");
78
+ } catch (err) {
79
+ post({
80
+ kind: "failed",
81
+ error: err instanceof Error ? err.message : String(err)
82
+ });
83
+ }
84
+ const row = readCheckpointRow(raw);
85
+ if (row === null) {
86
+ timer = setTimeout(tick, maxDelayMs);
87
+ return;
88
+ }
89
+ if (row.busy !== 0) {
90
+ post({
111
91
  kind: "checkpointed",
112
- reason: decision.reason,
113
- pendingFrames: decision.pendingFrames,
92
+ reason: first ? "first" : "scheduled",
93
+ appendedFrames: 0,
94
+ nextDelayMs: pollMs,
114
95
  walFrames: row.log,
115
96
  checkpointedFrames: row.checkpointed,
116
- busy: row.busy !== 0,
117
- ms: lastRunAt - startedAt
97
+ busy: true,
98
+ ms: Date.now() - startedAt
118
99
  });
100
+ timer = setTimeout(tick, pollMs);
101
+ return;
119
102
  }
120
- timer = setTimeout(tick, pollMs);
103
+ const current = {
104
+ at: Date.now(),
105
+ walFrames: row.log,
106
+ checkpointedFrames: row.checkpointed
107
+ };
108
+ const plan = nextCheckpointPlan({
109
+ previous,
110
+ current,
111
+ minDelayMs: pollMs,
112
+ maxDelayMs
113
+ });
114
+ post({
115
+ kind: "checkpointed",
116
+ reason: first ? "first" : "scheduled",
117
+ appendedFrames: plan.appendedFrames,
118
+ nextDelayMs: plan.nextDelayMs,
119
+ walFrames: row.log,
120
+ checkpointedFrames: row.checkpointed,
121
+ busy: row.busy !== 0,
122
+ ms: current.at - startedAt
123
+ });
124
+ previous = current;
125
+ first = false;
126
+ timer = setTimeout(tick, plan.nextDelayMs);
121
127
  };
122
128
  port.on("message", (message) => {
123
129
  if (!isWorkerStop(message)) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.318",
3
+ "version": "1.2.320",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",
@@ -1,92 +0,0 @@
1
- /**
2
- * Longest the worker lets pending frames sit before folding them, in ms. A
3
- * quiet database (a developer machine, an agent with little to persist) still
4
- * gets its WAL folded and its pages made durable within a minute.
5
- */
6
- var WAL_CHECKPOINT_MAX_INTERVAL_MS = 6e4;
7
- /** How often the worker looks at the wal-index, in ms. A 136-byte read. */
8
- var WAL_CHECKPOINT_POLL_MS = 1e3;
9
- /**
10
- * A checkpoint that took this long is reported at WARN with its numbers.
11
- * Matched to `SQLITE_SLOW_CALL_MS`: it is the same "the disk held us" event
12
- * the profiler used to file as a slow `set`, now on the thread that paid it.
13
- */
14
- var WAL_CHECKPOINT_SLOW_MS = 1e3;
15
- /** Should the worker run `wal_checkpoint(PASSIVE)` now? */
16
- function decideCheckpoint(input) {
17
- const intervalDue = input.sinceLastRunMs >= WAL_CHECKPOINT_MAX_INTERVAL_MS;
18
- if (input.mxFrame === null || input.nBackfill === null) return intervalDue ? {
19
- run: true,
20
- reason: "unknown-interval",
21
- pendingFrames: null
22
- } : {
23
- run: false,
24
- reason: "unknown-below-interval",
25
- pendingFrames: null
26
- };
27
- const pendingFrames = Math.max(0, input.mxFrame - input.nBackfill);
28
- if (pendingFrames === 0) return {
29
- run: false,
30
- reason: "nothing-pending",
31
- pendingFrames
32
- };
33
- if (pendingFrames >= 1e3) return {
34
- run: true,
35
- reason: "threshold",
36
- pendingFrames
37
- };
38
- if (intervalDue) return {
39
- run: true,
40
- reason: "interval",
41
- pendingFrames
42
- };
43
- return {
44
- run: false,
45
- reason: "below-threshold",
46
- pendingFrames
47
- };
48
- }
49
- /**
50
- * Byte offsets in the `-shm` file, from `wal.c` ("WAL-index file format"):
51
- * two copies of the 48-byte `WalIndexHdr` (copy 2 at 48), then `WalCkptInfo`
52
- * at 96 whose first field is `nBackfill`. `mxFrame` is at byte 16 of the
53
- * header. Both are u32 in the writer's native byte order — little-endian on
54
- * every x86-64 and arm64 node this ships to.
55
- */
56
- var WAL_INDEX_MXFRAME_OFFSET = 16;
57
- var WAL_INDEX_HDR_SIZE = 48;
58
- var WAL_INDEX_NBACKFILL_OFFSET = 96;
59
- /**
60
- * `mxFrame` and `nBackfill` from the first {@link WAL_INDEX_HEADER_READ_BYTES}
61
- * of the `-shm` file, or `null` when the buffer is short or the two header
62
- * copies disagree (a writer was updating them — SQLite's own readers retry on
63
- * exactly that comparison; this caller simply waits for the next poll).
64
- */
65
- function parseWalIndexHeader(buffer) {
66
- if (buffer.length < 100) return null;
67
- const copy1 = buffer.subarray(0, WAL_INDEX_HDR_SIZE);
68
- const copy2 = buffer.subarray(WAL_INDEX_HDR_SIZE, WAL_INDEX_HDR_SIZE * 2);
69
- if (!copy1.equals(copy2)) return null;
70
- return {
71
- mxFrame: buffer.readUInt32LE(WAL_INDEX_MXFRAME_OFFSET),
72
- nBackfill: buffer.readUInt32LE(WAL_INDEX_NBACKFILL_OFFSET)
73
- };
74
- }
75
- function isRecord(value) {
76
- return typeof value === "object" && value !== null;
77
- }
78
- /** Narrow a message off the worker port without a cast. */
79
- function isWorkerMessage(value) {
80
- if (!isRecord(value)) return false;
81
- switch (value["kind"]) {
82
- case "ready": return true;
83
- case "failed": return typeof value["error"] === "string";
84
- case "checkpointed": return typeof value["reason"] === "string" && (typeof value["pendingFrames"] === "number" || value["pendingFrames"] === null) && typeof value["walFrames"] === "number" && typeof value["checkpointedFrames"] === "number" && typeof value["busy"] === "boolean" && typeof value["ms"] === "number";
85
- default: return false;
86
- }
87
- }
88
- function isWorkerStop(value) {
89
- return isRecord(value) && value["kind"] === "stop";
90
- }
91
- //#endregion
92
- export { isWorkerStop as a, isWorkerMessage as i, WAL_CHECKPOINT_SLOW_MS as n, parseWalIndexHeader as o, decideCheckpoint as r, WAL_CHECKPOINT_POLL_MS as t };
@@ -1,127 +0,0 @@
1
- /**
2
- * Longest the worker lets pending frames sit before folding them, in ms. A
3
- * quiet database (a developer machine, an agent with little to persist) still
4
- * gets its WAL folded and its pages made durable within a minute.
5
- */
6
- var WAL_CHECKPOINT_MAX_INTERVAL_MS = 6e4;
7
- /** How often the worker looks at the wal-index, in ms. A 136-byte read. */
8
- var WAL_CHECKPOINT_POLL_MS = 1e3;
9
- /**
10
- * A checkpoint that took this long is reported at WARN with its numbers.
11
- * Matched to `SQLITE_SLOW_CALL_MS`: it is the same "the disk held us" event
12
- * the profiler used to file as a slow `set`, now on the thread that paid it.
13
- */
14
- var WAL_CHECKPOINT_SLOW_MS = 1e3;
15
- /** Should the worker run `wal_checkpoint(PASSIVE)` now? */
16
- function decideCheckpoint(input) {
17
- const intervalDue = input.sinceLastRunMs >= WAL_CHECKPOINT_MAX_INTERVAL_MS;
18
- if (input.mxFrame === null || input.nBackfill === null) return intervalDue ? {
19
- run: true,
20
- reason: "unknown-interval",
21
- pendingFrames: null
22
- } : {
23
- run: false,
24
- reason: "unknown-below-interval",
25
- pendingFrames: null
26
- };
27
- const pendingFrames = Math.max(0, input.mxFrame - input.nBackfill);
28
- if (pendingFrames === 0) return {
29
- run: false,
30
- reason: "nothing-pending",
31
- pendingFrames
32
- };
33
- if (pendingFrames >= 1e3) return {
34
- run: true,
35
- reason: "threshold",
36
- pendingFrames
37
- };
38
- if (intervalDue) return {
39
- run: true,
40
- reason: "interval",
41
- pendingFrames
42
- };
43
- return {
44
- run: false,
45
- reason: "below-threshold",
46
- pendingFrames
47
- };
48
- }
49
- /**
50
- * Byte offsets in the `-shm` file, from `wal.c` ("WAL-index file format"):
51
- * two copies of the 48-byte `WalIndexHdr` (copy 2 at 48), then `WalCkptInfo`
52
- * at 96 whose first field is `nBackfill`. `mxFrame` is at byte 16 of the
53
- * header. Both are u32 in the writer's native byte order — little-endian on
54
- * every x86-64 and arm64 node this ships to.
55
- */
56
- var WAL_INDEX_MXFRAME_OFFSET = 16;
57
- var WAL_INDEX_HDR_SIZE = 48;
58
- var WAL_INDEX_NBACKFILL_OFFSET = 96;
59
- /**
60
- * `mxFrame` and `nBackfill` from the first {@link WAL_INDEX_HEADER_READ_BYTES}
61
- * of the `-shm` file, or `null` when the buffer is short or the two header
62
- * copies disagree (a writer was updating them — SQLite's own readers retry on
63
- * exactly that comparison; this caller simply waits for the next poll).
64
- */
65
- function parseWalIndexHeader(buffer) {
66
- if (buffer.length < 100) return null;
67
- const copy1 = buffer.subarray(0, WAL_INDEX_HDR_SIZE);
68
- const copy2 = buffer.subarray(WAL_INDEX_HDR_SIZE, WAL_INDEX_HDR_SIZE * 2);
69
- if (!copy1.equals(copy2)) return null;
70
- return {
71
- mxFrame: buffer.readUInt32LE(WAL_INDEX_MXFRAME_OFFSET),
72
- nBackfill: buffer.readUInt32LE(WAL_INDEX_NBACKFILL_OFFSET)
73
- };
74
- }
75
- function isRecord(value) {
76
- return typeof value === "object" && value !== null;
77
- }
78
- /** Narrow a message off the worker port without a cast. */
79
- function isWorkerMessage(value) {
80
- if (!isRecord(value)) return false;
81
- switch (value["kind"]) {
82
- case "ready": return true;
83
- case "failed": return typeof value["error"] === "string";
84
- case "checkpointed": return typeof value["reason"] === "string" && (typeof value["pendingFrames"] === "number" || value["pendingFrames"] === null) && typeof value["walFrames"] === "number" && typeof value["checkpointedFrames"] === "number" && typeof value["busy"] === "boolean" && typeof value["ms"] === "number";
85
- default: return false;
86
- }
87
- }
88
- function isWorkerStop(value) {
89
- return isRecord(value) && value["kind"] === "stop";
90
- }
91
- //#endregion
92
- Object.defineProperty(exports, "WAL_CHECKPOINT_POLL_MS", {
93
- enumerable: true,
94
- get: function() {
95
- return WAL_CHECKPOINT_POLL_MS;
96
- }
97
- });
98
- Object.defineProperty(exports, "WAL_CHECKPOINT_SLOW_MS", {
99
- enumerable: true,
100
- get: function() {
101
- return WAL_CHECKPOINT_SLOW_MS;
102
- }
103
- });
104
- Object.defineProperty(exports, "decideCheckpoint", {
105
- enumerable: true,
106
- get: function() {
107
- return decideCheckpoint;
108
- }
109
- });
110
- Object.defineProperty(exports, "isWorkerMessage", {
111
- enumerable: true,
112
- get: function() {
113
- return isWorkerMessage;
114
- }
115
- });
116
- Object.defineProperty(exports, "isWorkerStop", {
117
- enumerable: true,
118
- get: function() {
119
- return isWorkerStop;
120
- }
121
- });
122
- Object.defineProperty(exports, "parseWalIndexHeader", {
123
- enumerable: true,
124
- get: function() {
125
- return parseWalIndexHeader;
126
- }
127
- });