@camstack/system 1.2.57 → 1.2.58

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 (64) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.mjs +1 -1
  3. package/dist/addon-utils.js +1 -1
  4. package/dist/addon-utils.mjs +1 -1
  5. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +2 -2
  6. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +2 -2
  7. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +2 -2
  8. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +2 -2
  9. package/dist/builtins/alerts/alerts.addon.js +1 -1
  10. package/dist/builtins/alerts/alerts.addon.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/console-logging/index.js +1 -1
  14. package/dist/builtins/console-logging/index.mjs +1 -1
  15. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  16. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  17. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  18. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  19. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  20. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  21. package/dist/builtins/hub-forwarder/index.js +1 -1
  22. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  23. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  24. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  25. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  26. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  27. package/dist/builtins/local-network/local-network.addon.js +1 -1
  28. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  29. package/dist/builtins/loki-logging/index.js +1 -1
  30. package/dist/builtins/loki-logging/index.mjs +1 -1
  31. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  32. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  33. package/dist/builtins/platform-probe/index.js +1 -1
  34. package/dist/builtins/platform-probe/index.mjs +1 -1
  35. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  36. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  37. package/dist/builtins/snapshot/index.js +1 -1
  38. package/dist/builtins/snapshot/index.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +2 -2
  40. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +2 -2
  41. package/dist/builtins/sqlite-storage/index.d.ts +3 -0
  42. package/dist/builtins/sqlite-storage/index.js +8 -0
  43. package/dist/builtins/sqlite-storage/index.mjs +2 -2
  44. package/dist/builtins/sqlite-storage/sqlite-pragmas.d.ts +85 -0
  45. package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +1 -0
  46. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +304 -1
  47. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +297 -2
  48. package/dist/builtins/sqlite-storage/wal-maintenance.d.ts +113 -0
  49. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  50. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  51. package/dist/builtins/system-config/system-config.addon.js +1 -1
  52. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  53. package/dist/builtins/winston-logging/index.js +1 -1
  54. package/dist/builtins/winston-logging/index.mjs +1 -1
  55. package/dist/{dist-BupxnZOi.js → dist-Chl2MvMd.js} +632 -17
  56. package/dist/{dist-DhL_zGRT.mjs → dist-UrXyJ9Zm.mjs} +632 -17
  57. package/dist/index.js +4 -4
  58. package/dist/index.mjs +5 -5
  59. package/dist/kernel/transport/frame-codec.d.ts +43 -2
  60. package/dist/{manifest-python-deps-Ck4-9K9m.mjs → manifest-python-deps-CX5tGRqf.mjs} +95 -10
  61. package/dist/{manifest-python-deps-BqE5j0-O.js → manifest-python-deps-d4acYXVV.js} +95 -10
  62. package/package.json +1 -1
  63. package/dist/{model-download-service-hf0ookyy.js → model-download-service-Cpc6SWzV.js} +1 -1
  64. package/dist/{model-download-service-Cp9f4dk6.mjs → model-download-service-D8B-4ktF.mjs} +1 -1
package/dist/index.js CHANGED
@@ -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-BupxnZOi.js");
4
- const require_model_download_service = require("./model-download-service-hf0ookyy.js");
5
- const require_manifest_python_deps = require("./manifest-python-deps-BqE5j0-O.js");
3
+ const require_dist = require("./dist-Chl2MvMd.js");
4
+ const require_model_download_service = require("./model-download-service-Cpc6SWzV.js");
5
+ const require_manifest_python_deps = require("./manifest-python-deps-d4acYXVV.js");
6
6
  const require_resource_monitor = require("./resource-monitor-DNNomR-i.js");
7
7
  const require_builtins_sqlite_storage_filesystem_storage_addon = require("./builtins/sqlite-storage/filesystem-storage.addon.js");
8
8
  const require_builtins_sqlite_storage_sqlite_settings_addon = require("./builtins/sqlite-storage/sqlite-settings.addon.js");
@@ -32,13 +32,13 @@ let node_v8 = require("node:v8");
32
32
  node_v8 = require_chunk.__toESM(node_v8);
33
33
  let _camstack_types_node = require("@camstack/types/node");
34
34
  let zod = require("zod");
35
+ let node_crypto = require("node:crypto");
35
36
  let node_http = require("node:http");
36
37
  let node_fs = require("node:fs");
37
38
  let node_fs$1 = require_chunk.__toESM(node_fs, 1);
38
39
  node_fs = require_chunk.__toESM(node_fs);
39
40
  let node_path = require("node:path");
40
41
  node_path = require_chunk.__toESM(node_path, 1);
41
- let node_crypto = require("node:crypto");
42
42
  let _camstack_types_addon = require("@camstack/types/addon");
43
43
  let node_child_process = require("node:child_process");
44
44
  let node_util = require("node:util");
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM$1, r as __exportAll, t as __commonJSMin$1 } from "./chunk-CNf5ZN-e.mjs";
2
- import { B as kebabToCamel, Ct as asString$1, Dt as expandCapMethods, Ft as scopeKey, G as looseSchema, I as isArrayOutputSchema, J as objectInputDeclaresAddonId, L as isCollectionArrayMethod, Lt as EventCategory$1, Mt as parseJsonUnknown$1, N as extractNestedAddonId, Nt as readinessKey, Pt as resolveCapMount, R as isObjectInput, St as asNumber, Tt as emitDownForOwnedCaps, V as lifecycleJobSchema, W as logLevelAtMost, Z as procedureAuthKey, c as METHOD_ACCESS_MAP, ft as DATAPLANE_SECRET_HEADER$1, h as addonSettingsCapability, jt as parseJsonObject, l as RUNTIME_DEFAULTS, mt as DEVICE_STATUS_METHOD, pt as DEVICE_SETTINGS_CONTRIBUTION_METHODS, t as ALL_CAPABILITY_DEFINITIONS, ut as errMsg$1, vt as ReadinessRegistry, wt as createEvent, xt as asJsonObject$1, yt as ReadinessTimeoutError, z as isVoidInput } from "./dist-DhL_zGRT.mjs";
3
- import { a as downloadModel, c as getModelFilePath, d as contentTypeFor, f as createAuthenticatedFileServer, h as resolveFilePath, i as downloadFile, l as isModelDownloaded, m as parseTokenizedUrl, n as collectModelFiles, o as ensureModel, p as parseRangeHeader, r as deleteModelFromDisk, s as fetchJson, t as ModelDownloadService, u as createFileDataPlaneHandler } from "./model-download-service-Cp9f4dk6.mjs";
4
- import { $ as buildNativeCapProxy, A as createHubCapForwardService, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as resolveAddonClass, D as localProviderLink, E as ipcParentLink, F as createUdsLogger, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, N as createUdsEventBus, O as HUB_CAP_FWD_ACTION, P as udsChildLogToWorkerEntry, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as runNpm, T as ipcChildLink, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, k as HUB_CAP_FWD_SERVICE, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-Ck4-9K9m.mjs";
2
+ import { B as kebabToCamel, Ct as asString$1, Dt as expandCapMethods, Ft as scopeKey, G as looseSchema, I as isArrayOutputSchema, J as objectInputDeclaresAddonId, L as isCollectionArrayMethod, Lt as EventCategory$1, Mt as parseJsonUnknown$1, N as extractNestedAddonId, Nt as readinessKey, Pt as resolveCapMount, R as isObjectInput, St as asNumber, Tt as emitDownForOwnedCaps, V as lifecycleJobSchema, W as logLevelAtMost, Z as procedureAuthKey, c as METHOD_ACCESS_MAP, ft as DATAPLANE_SECRET_HEADER$1, h as addonSettingsCapability, jt as parseJsonObject, l as RUNTIME_DEFAULTS, mt as DEVICE_STATUS_METHOD, pt as DEVICE_SETTINGS_CONTRIBUTION_METHODS, t as ALL_CAPABILITY_DEFINITIONS, ut as errMsg$1, vt as ReadinessRegistry, wt as createEvent, xt as asJsonObject$1, yt as ReadinessTimeoutError, z as isVoidInput } from "./dist-UrXyJ9Zm.mjs";
3
+ import { a as downloadModel, c as getModelFilePath, d as contentTypeFor, f as createAuthenticatedFileServer, h as resolveFilePath, i as downloadFile, l as isModelDownloaded, m as parseTokenizedUrl, n as collectModelFiles, o as ensureModel, p as parseRangeHeader, r as deleteModelFromDisk, s as fetchJson, t as ModelDownloadService, u as createFileDataPlaneHandler } from "./model-download-service-D8B-4ktF.mjs";
4
+ import { $ as buildNativeCapProxy, A as createHubCapForwardService, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as resolveAddonClass, D as localProviderLink, E as ipcParentLink, F as createUdsLogger, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, N as createUdsEventBus, O as HUB_CAP_FWD_ACTION, P as udsChildLogToWorkerEntry, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as runNpm, T as ipcChildLink, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, k as HUB_CAP_FWD_SERVICE, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-CX5tGRqf.mjs";
5
5
  import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-BkP504Vq.mjs";
6
6
  import { FilesystemStorageAddon, t as FilesystemStorageProvider } from "./builtins/sqlite-storage/filesystem-storage.addon.mjs";
7
- import { SqliteSettingsAddon, t as SqliteSettingsBackend } from "./builtins/sqlite-storage/sqlite-settings.addon.mjs";
7
+ import { SqliteSettingsAddon, o as SqliteSettingsBackend } from "./builtins/sqlite-storage/sqlite-settings.addon.mjs";
8
8
  import { ConfigStore, DeviceStore } from "./builtins/sqlite-storage/index.mjs";
9
9
  import { t as formatLogLine } from "./formatter-B7qW8bPJ.mjs";
10
10
  import { WinstonDestination, WinstonLoggingAddon } from "./builtins/winston-logging/index.mjs";
@@ -30,12 +30,12 @@ import { t as CustomActionRegistry } from "./custom-action-registry-F__gp_VX.mjs
30
30
  import * as v8 from "node:v8";
31
31
  import { PYTHON_VERSION, buildBinaryPath, downloadBinary, ensureBinary, ensureFfmpeg, ensurePython, findInPath, getFfmpegDownloadUrl, getPlatformInfo, getPythonDownloadUrl, installPythonPackages, installPythonRequirements } from "@camstack/types/node";
32
32
  import { z } from "zod";
33
+ import { X509Certificate, createHash, randomUUID, timingSafeEqual } from "node:crypto";
33
34
  import { request } from "node:http";
34
35
  import * as fs$17 from "node:fs";
35
36
  import { accessSync, constants, existsSync, mkdirSync, readFileSync } from "node:fs";
36
37
  import * as path$39 from "node:path";
37
38
  import { dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
38
- import { X509Certificate, createHash, randomUUID, timingSafeEqual } from "node:crypto";
39
39
  import { asJsonObject, errMsg, parseJsonUnknown } from "@camstack/types/addon";
40
40
  import { execFile, spawn } from "node:child_process";
41
41
  import * as util$10 from "node:util";
@@ -1,7 +1,48 @@
1
1
  /** Encode a value as a length-prefixed (4-byte big-endian) MsgPack frame. */
2
2
  export declare function encodeFrame(value: unknown): Buffer;
3
- /** Reassembles length-prefixed MsgPack frames from a byte stream. */
3
+ /**
4
+ * Reassembles length-prefixed MsgPack frames from a byte stream.
5
+ *
6
+ * **Why this is a chunk LIST and not a growing Buffer.** The original
7
+ * accumulator did `this.buf = Buffer.concat([this.buf, chunk])` on every
8
+ * socket `data` event. A frame of N bytes delivered in C-byte pieces
9
+ * therefore copied ~N²/2C bytes: a 4 MiB native crop arriving in 64 KiB
10
+ * chunks is ~128 MiB of memcpy plus 64 intermediate Buffer allocations,
11
+ * synchronously, on the thread that also serves every tRPC request. Media
12
+ * transits this router constantly, and hub-main was measured busy 82.2 s out
13
+ * of 120 with 22 uninterrupted blocks ≥1 s.
14
+ *
15
+ * Here the pending chunks are kept as-is and joined ONCE, when a frame is
16
+ * complete — and not even then in the common case, because a frame that
17
+ * arrived whole inside one chunk is sliced out with zero copying. Total work
18
+ * is linear in the bytes received.
19
+ *
20
+ * The wire contract is unchanged: 4-byte big-endian length, then that many
21
+ * MsgPack bytes; a partial frame is buffered; several frames in one chunk are
22
+ * all returned; a frame spanning any number of chunks is reassembled. There
23
+ * is still no upper bound on a declared length — an oversized prefix parks
24
+ * bytes in `chunks` until the peer completes or the socket closes, exactly as
25
+ * before. Bounding it is a transport-protocol change, not a decoder cleanup.
26
+ */
4
27
  export declare class FrameDecoder {
5
- private buf;
28
+ /** Unconsumed bytes, oldest first. `chunks[0]` starts at a frame boundary. */
29
+ private chunks;
30
+ /** Sum of `chunks[i].byteLength` — tracked so length checks cost nothing. */
31
+ private buffered;
6
32
  push(chunk: Buffer): unknown[];
33
+ /**
34
+ * Read the frame length without consuming it. The prefix itself can land
35
+ * across a chunk boundary (a 3-byte first chunk is a real socket event), so
36
+ * the slow path walks bytes until it has all four.
37
+ */
38
+ private readLength;
39
+ /**
40
+ * Consume and return exactly `n` contiguous bytes.
41
+ *
42
+ * Three cases, cheapest first: the head chunk already holds the whole frame
43
+ * (no copy at all, just a view); the head chunk holds more than the frame
44
+ * (a view plus a rebased head); the frame spans chunks (ONE `Buffer.concat`
45
+ * sized to the frame — the single copy this class is allowed).
46
+ */
47
+ private take;
7
48
  }
@@ -1,10 +1,10 @@
1
1
  import { ensureBinary, ensureFfmpeg, ensurePython, installPythonPackages, installPythonRequirements } from "@camstack/types/node";
2
+ import * as crypto$1 from "node:crypto";
3
+ import { randomBytes, randomUUID } from "node:crypto";
2
4
  import { createServer } from "node:http";
3
5
  import * as fs from "node:fs";
4
6
  import * as path$1 from "node:path";
5
7
  import { isAbsolute, join } from "node:path";
6
- import * as crypto$1 from "node:crypto";
7
- import { randomBytes, randomUUID } from "node:crypto";
8
8
  import { DATAPLANE_SECRET_HEADER, DeviceType, DisposerChain, EventCategory, ReadinessRegistry, asJsonObject, asString, createDeviceProxy, deviceOpsCapability, emitReadiness, errMsg, expandCapMethods, readNodePin, scopeKey, sleep, withCapInputDefaults } from "@camstack/types/addon";
9
9
  import { execFile } from "node:child_process";
10
10
  import { promisify } from "node:util";
@@ -3518,20 +3518,105 @@ function encodeFrame(value) {
3518
3518
  Buffer.from(body.buffer, body.byteOffset, body.byteLength).copy(out, 4);
3519
3519
  return out;
3520
3520
  }
3521
- /** Reassembles length-prefixed MsgPack frames from a byte stream. */
3521
+ /** Bytes in the big-endian length prefix that opens every frame. */
3522
+ var HEADER_BYTES = 4;
3523
+ /**
3524
+ * Reassembles length-prefixed MsgPack frames from a byte stream.
3525
+ *
3526
+ * **Why this is a chunk LIST and not a growing Buffer.** The original
3527
+ * accumulator did `this.buf = Buffer.concat([this.buf, chunk])` on every
3528
+ * socket `data` event. A frame of N bytes delivered in C-byte pieces
3529
+ * therefore copied ~N²/2C bytes: a 4 MiB native crop arriving in 64 KiB
3530
+ * chunks is ~128 MiB of memcpy plus 64 intermediate Buffer allocations,
3531
+ * synchronously, on the thread that also serves every tRPC request. Media
3532
+ * transits this router constantly, and hub-main was measured busy 82.2 s out
3533
+ * of 120 with 22 uninterrupted blocks ≥1 s.
3534
+ *
3535
+ * Here the pending chunks are kept as-is and joined ONCE, when a frame is
3536
+ * complete — and not even then in the common case, because a frame that
3537
+ * arrived whole inside one chunk is sliced out with zero copying. Total work
3538
+ * is linear in the bytes received.
3539
+ *
3540
+ * The wire contract is unchanged: 4-byte big-endian length, then that many
3541
+ * MsgPack bytes; a partial frame is buffered; several frames in one chunk are
3542
+ * all returned; a frame spanning any number of chunks is reassembled. There
3543
+ * is still no upper bound on a declared length — an oversized prefix parks
3544
+ * bytes in `chunks` until the peer completes or the socket closes, exactly as
3545
+ * before. Bounding it is a transport-protocol change, not a decoder cleanup.
3546
+ */
3522
3547
  var FrameDecoder = class {
3523
- buf = Buffer.alloc(0);
3548
+ /** Unconsumed bytes, oldest first. `chunks[0]` starts at a frame boundary. */
3549
+ chunks = [];
3550
+ /** Sum of `chunks[i].byteLength` — tracked so length checks cost nothing. */
3551
+ buffered = 0;
3524
3552
  push(chunk) {
3525
- this.buf = this.buf.byteLength === 0 ? chunk : Buffer.concat([this.buf, chunk]);
3553
+ if (chunk.byteLength > 0) {
3554
+ this.chunks.push(chunk);
3555
+ this.buffered += chunk.byteLength;
3556
+ }
3526
3557
  const frames = [];
3527
- while (this.buf.byteLength >= 4) {
3528
- const len = this.buf.readUInt32BE(0);
3529
- if (this.buf.byteLength < 4 + len) break;
3530
- frames.push(decode(this.buf.subarray(4, 4 + len)));
3531
- this.buf = this.buf.subarray(4 + len);
3558
+ while (this.buffered >= HEADER_BYTES) {
3559
+ const total = HEADER_BYTES + this.readLength();
3560
+ if (this.buffered < total) break;
3561
+ frames.push(decode(this.take(total).subarray(HEADER_BYTES)));
3532
3562
  }
3533
3563
  return frames;
3534
3564
  }
3565
+ /**
3566
+ * Read the frame length without consuming it. The prefix itself can land
3567
+ * across a chunk boundary (a 3-byte first chunk is a real socket event), so
3568
+ * the slow path walks bytes until it has all four.
3569
+ */
3570
+ readLength() {
3571
+ const head = this.chunks[0];
3572
+ if (head === void 0) throw new Error("FrameDecoder: buffered bytes with no chunks");
3573
+ if (head.byteLength >= HEADER_BYTES) return head.readUInt32BE(0);
3574
+ let value = 0;
3575
+ let seen = 0;
3576
+ for (const c of this.chunks) {
3577
+ for (let i = 0; i < c.byteLength && seen < HEADER_BYTES; i++, seen++) value = value * 256 + (c[i] ?? 0);
3578
+ if (seen === HEADER_BYTES) break;
3579
+ }
3580
+ return value;
3581
+ }
3582
+ /**
3583
+ * Consume and return exactly `n` contiguous bytes.
3584
+ *
3585
+ * Three cases, cheapest first: the head chunk already holds the whole frame
3586
+ * (no copy at all, just a view); the head chunk holds more than the frame
3587
+ * (a view plus a rebased head); the frame spans chunks (ONE `Buffer.concat`
3588
+ * sized to the frame — the single copy this class is allowed).
3589
+ */
3590
+ take(n) {
3591
+ const head = this.chunks[0];
3592
+ if (head === void 0) throw new Error("FrameDecoder: take() with no chunks");
3593
+ this.buffered -= n;
3594
+ if (head.byteLength === n) {
3595
+ this.chunks.shift();
3596
+ return head;
3597
+ }
3598
+ if (head.byteLength > n) {
3599
+ this.chunks[0] = head.subarray(n);
3600
+ return head.subarray(0, n);
3601
+ }
3602
+ const parts = [];
3603
+ let collected = 0;
3604
+ while (collected < n) {
3605
+ const next = this.chunks[0];
3606
+ if (next === void 0) throw new Error("FrameDecoder: ran out of chunks mid-frame");
3607
+ const remaining = n - collected;
3608
+ if (next.byteLength <= remaining) {
3609
+ parts.push(next);
3610
+ collected += next.byteLength;
3611
+ this.chunks.shift();
3612
+ continue;
3613
+ }
3614
+ parts.push(next.subarray(0, remaining));
3615
+ this.chunks[0] = next.subarray(remaining);
3616
+ collected = n;
3617
+ }
3618
+ return Buffer.concat(parts, n);
3619
+ }
3535
3620
  };
3536
3621
  //#endregion
3537
3622
  //#region src/kernel/transport/local-endpoint-path.ts
@@ -1,12 +1,12 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
2
  let _camstack_types_node = require("@camstack/types/node");
3
+ let node_crypto = require("node:crypto");
4
+ node_crypto = require_chunk.__toESM(node_crypto);
3
5
  let node_http = require("node:http");
4
6
  let node_fs = require("node:fs");
5
7
  node_fs = require_chunk.__toESM(node_fs);
6
8
  let node_path = require("node:path");
7
9
  node_path = require_chunk.__toESM(node_path);
8
- let node_crypto = require("node:crypto");
9
- node_crypto = require_chunk.__toESM(node_crypto);
10
10
  let _camstack_types_addon = require("@camstack/types/addon");
11
11
  let node_child_process = require("node:child_process");
12
12
  let node_util = require("node:util");
@@ -3520,20 +3520,105 @@ function encodeFrame(value) {
3520
3520
  Buffer.from(body.buffer, body.byteOffset, body.byteLength).copy(out, 4);
3521
3521
  return out;
3522
3522
  }
3523
- /** Reassembles length-prefixed MsgPack frames from a byte stream. */
3523
+ /** Bytes in the big-endian length prefix that opens every frame. */
3524
+ var HEADER_BYTES = 4;
3525
+ /**
3526
+ * Reassembles length-prefixed MsgPack frames from a byte stream.
3527
+ *
3528
+ * **Why this is a chunk LIST and not a growing Buffer.** The original
3529
+ * accumulator did `this.buf = Buffer.concat([this.buf, chunk])` on every
3530
+ * socket `data` event. A frame of N bytes delivered in C-byte pieces
3531
+ * therefore copied ~N²/2C bytes: a 4 MiB native crop arriving in 64 KiB
3532
+ * chunks is ~128 MiB of memcpy plus 64 intermediate Buffer allocations,
3533
+ * synchronously, on the thread that also serves every tRPC request. Media
3534
+ * transits this router constantly, and hub-main was measured busy 82.2 s out
3535
+ * of 120 with 22 uninterrupted blocks ≥1 s.
3536
+ *
3537
+ * Here the pending chunks are kept as-is and joined ONCE, when a frame is
3538
+ * complete — and not even then in the common case, because a frame that
3539
+ * arrived whole inside one chunk is sliced out with zero copying. Total work
3540
+ * is linear in the bytes received.
3541
+ *
3542
+ * The wire contract is unchanged: 4-byte big-endian length, then that many
3543
+ * MsgPack bytes; a partial frame is buffered; several frames in one chunk are
3544
+ * all returned; a frame spanning any number of chunks is reassembled. There
3545
+ * is still no upper bound on a declared length — an oversized prefix parks
3546
+ * bytes in `chunks` until the peer completes or the socket closes, exactly as
3547
+ * before. Bounding it is a transport-protocol change, not a decoder cleanup.
3548
+ */
3524
3549
  var FrameDecoder = class {
3525
- buf = Buffer.alloc(0);
3550
+ /** Unconsumed bytes, oldest first. `chunks[0]` starts at a frame boundary. */
3551
+ chunks = [];
3552
+ /** Sum of `chunks[i].byteLength` — tracked so length checks cost nothing. */
3553
+ buffered = 0;
3526
3554
  push(chunk) {
3527
- this.buf = this.buf.byteLength === 0 ? chunk : Buffer.concat([this.buf, chunk]);
3555
+ if (chunk.byteLength > 0) {
3556
+ this.chunks.push(chunk);
3557
+ this.buffered += chunk.byteLength;
3558
+ }
3528
3559
  const frames = [];
3529
- while (this.buf.byteLength >= 4) {
3530
- const len = this.buf.readUInt32BE(0);
3531
- if (this.buf.byteLength < 4 + len) break;
3532
- frames.push(decode(this.buf.subarray(4, 4 + len)));
3533
- this.buf = this.buf.subarray(4 + len);
3560
+ while (this.buffered >= HEADER_BYTES) {
3561
+ const total = HEADER_BYTES + this.readLength();
3562
+ if (this.buffered < total) break;
3563
+ frames.push(decode(this.take(total).subarray(HEADER_BYTES)));
3534
3564
  }
3535
3565
  return frames;
3536
3566
  }
3567
+ /**
3568
+ * Read the frame length without consuming it. The prefix itself can land
3569
+ * across a chunk boundary (a 3-byte first chunk is a real socket event), so
3570
+ * the slow path walks bytes until it has all four.
3571
+ */
3572
+ readLength() {
3573
+ const head = this.chunks[0];
3574
+ if (head === void 0) throw new Error("FrameDecoder: buffered bytes with no chunks");
3575
+ if (head.byteLength >= HEADER_BYTES) return head.readUInt32BE(0);
3576
+ let value = 0;
3577
+ let seen = 0;
3578
+ for (const c of this.chunks) {
3579
+ for (let i = 0; i < c.byteLength && seen < HEADER_BYTES; i++, seen++) value = value * 256 + (c[i] ?? 0);
3580
+ if (seen === HEADER_BYTES) break;
3581
+ }
3582
+ return value;
3583
+ }
3584
+ /**
3585
+ * Consume and return exactly `n` contiguous bytes.
3586
+ *
3587
+ * Three cases, cheapest first: the head chunk already holds the whole frame
3588
+ * (no copy at all, just a view); the head chunk holds more than the frame
3589
+ * (a view plus a rebased head); the frame spans chunks (ONE `Buffer.concat`
3590
+ * sized to the frame — the single copy this class is allowed).
3591
+ */
3592
+ take(n) {
3593
+ const head = this.chunks[0];
3594
+ if (head === void 0) throw new Error("FrameDecoder: take() with no chunks");
3595
+ this.buffered -= n;
3596
+ if (head.byteLength === n) {
3597
+ this.chunks.shift();
3598
+ return head;
3599
+ }
3600
+ if (head.byteLength > n) {
3601
+ this.chunks[0] = head.subarray(n);
3602
+ return head.subarray(0, n);
3603
+ }
3604
+ const parts = [];
3605
+ let collected = 0;
3606
+ while (collected < n) {
3607
+ const next = this.chunks[0];
3608
+ if (next === void 0) throw new Error("FrameDecoder: ran out of chunks mid-frame");
3609
+ const remaining = n - collected;
3610
+ if (next.byteLength <= remaining) {
3611
+ parts.push(next);
3612
+ collected += next.byteLength;
3613
+ this.chunks.shift();
3614
+ continue;
3615
+ }
3616
+ parts.push(next.subarray(0, remaining));
3617
+ this.chunks[0] = next.subarray(remaining);
3618
+ collected = n;
3619
+ }
3620
+ return Buffer.concat(parts, n);
3621
+ }
3537
3622
  };
3538
3623
  //#endregion
3539
3624
  //#region src/kernel/transport/local-endpoint-path.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.2.57",
3
+ "version": "1.2.58",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",
@@ -1,10 +1,10 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
+ let node_crypto = require("node:crypto");
2
3
  let node_http = require("node:http");
3
4
  let node_fs = require("node:fs");
4
5
  node_fs = require_chunk.__toESM(node_fs);
5
6
  let node_path = require("node:path");
6
7
  node_path = require_chunk.__toESM(node_path);
7
- let node_crypto = require("node:crypto");
8
8
  //#region src/http/authenticated-file-server.ts
9
9
  /**
10
10
  * Authenticated data-plane file-server — a shared host primitive for addons that
@@ -1,9 +1,9 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { createServer } from "node:http";
2
3
  import * as fs from "node:fs";
3
4
  import { createReadStream, promises } from "node:fs";
4
5
  import * as path$1 from "node:path";
5
6
  import path from "node:path";
6
- import { createHash } from "node:crypto";
7
7
  //#region src/http/authenticated-file-server.ts
8
8
  /**
9
9
  * Authenticated data-plane file-server — a shared host primitive for addons that