@fml-inc/fml 0.8.7 → 0.8.10-dev.1

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.
@@ -5,11 +5,11 @@ import {
5
5
  isWinShim,
6
6
  quoteWinArg,
7
7
  resolveBin
8
- } from "./chunk-XFALHTDL.js";
8
+ } from "./chunk-5SI6S4HW.js";
9
9
  import {
10
10
  createHookSyncLoop,
11
11
  getActiveHookDestinationBinding
12
- } from "./chunk-PJWMSHHZ.js";
12
+ } from "./chunk-WE7MAWAW.js";
13
13
  import {
14
14
  OBSERVER_PROTOCOL_VERSION,
15
15
  OBSERVER_SERVICE_NAME,
@@ -30,16 +30,16 @@ import {
30
30
  removeObserverStateIfOwned,
31
31
  retireHookInboxEntry,
32
32
  updateObserverState
33
- } from "./chunk-4YLSKFDU.js";
33
+ } from "./chunk-5PEFBQNX.js";
34
34
  import "./chunk-7V7G7NVF.js";
35
35
  import {
36
36
  ObserverSyncCycleAbortedError,
37
37
  runObserverSyncCycleSubprocess
38
- } from "./chunk-6BTC3S7C.js";
38
+ } from "./chunk-2DGHAVVR.js";
39
39
  import {
40
40
  createCliRawSync,
41
41
  invalidateCliRawSyncDiscoveryCache
42
- } from "./chunk-5JJQWV4G.js";
42
+ } from "./chunk-JPHN4VKM.js";
43
43
  import "./chunk-F57IFY3K.js";
44
44
  import {
45
45
  FML_OBSERVER_AUTH_TOKEN_FILE,
@@ -58,7 +58,7 @@ import {
58
58
  getSyncDestinationAuthBinding,
59
59
  getValidToken,
60
60
  observerOnlyOverrideWarning
61
- } from "./chunk-B3H2YWPJ.js";
61
+ } from "./chunk-HDAFEHUY.js";
62
62
  import "./chunk-MLKGABMK.js";
63
63
 
64
64
  // src/observer-server.ts
@@ -225,7 +225,6 @@ var MAX_ERROR_TEXT_BYTES = 1e3;
225
225
  var MAX_REJECTION_REASON_BYTES = 3 * 1024;
226
226
  var MAX_OTLP_RESPONSE_BYTES = 64 * 1024;
227
227
  var MAX_OTLP_UPLOAD_ATTEMPTS = 10;
228
- var MAX_OTLP_RETRY_AGE_MS = 24 * 60 * 6e4;
229
228
  var OUTBOX_BATCH_SIZE = 1;
230
229
  var DRAIN_INTERVAL_MS = 25;
231
230
  function combineWarnings(...warnings) {
@@ -675,6 +674,7 @@ async function runOtlpSyncCycle(opts) {
675
674
  if (totalPending === 0) {
676
675
  return { kind: "drained", pending: 0, uploaded: 0 };
677
676
  }
677
+ const maxAgeMs = opts.database.getOtlpSpoolStatus().maxAgeMs;
678
678
  const destinationResolution = await resolveDestination();
679
679
  if (destinationResolution.kind !== "ready") {
680
680
  return {
@@ -736,7 +736,7 @@ async function runOtlpSyncCycle(opts) {
736
736
  };
737
737
  for (const envelope of envelopes) {
738
738
  if (opts.signal?.aborted) throw opts.signal.reason;
739
- if (cycleStartedAtMs - envelope.capturedAtMs >= MAX_OTLP_RETRY_AGE_MS) {
739
+ if (cycleStartedAtMs - envelope.capturedAtMs >= maxAgeMs) {
740
740
  dropped += opts.database.dropOtlpEnvelopes(
741
741
  destination,
742
742
  [
@@ -1046,6 +1046,17 @@ function parseOtlpSpoolMaxBytes(configured) {
1046
1046
  }
1047
1047
  return bytes;
1048
1048
  }
1049
+ function parseOtlpSpoolMaxAgeMs(configured) {
1050
+ if (configured === void 0) return void 0;
1051
+ const value = configured.trim();
1052
+ const milliseconds = Number(value) * 60 * 60 * 1e3;
1053
+ if (!POSITIVE_INTEGER_PATTERN.test(value) || !Number.isSafeInteger(milliseconds)) {
1054
+ throw new Error(
1055
+ "FML_OTLP_SPOOL_MAX_AGE_HOURS must be a positive safe integer number of hours"
1056
+ );
1057
+ }
1058
+ return milliseconds;
1059
+ }
1049
1060
 
1050
1061
  // src/observer/sync-loop.ts
1051
1062
  var OBSERVER_IDLE_INTERVAL_MS = 5 * 6e4;
@@ -1348,7 +1359,8 @@ var HOOK_INBOX_DRAIN_INTERVAL_MS = 15e3;
1348
1359
  var HOOK_INBOX_DRAIN_BUDGET_MS = 5e3;
1349
1360
  var HOOK_INBOX_DRAIN_ENTRIES = 100;
1350
1361
  var HOOK_INBOX_DRAIN_BULK_ENTRIES = 1e3;
1351
- var HOOK_QUARANTINE_PRUNE_INTERVAL_MS = 5 * 60 * 1e3;
1362
+ var SPOOL_CLEANUP_INTERVAL_MS = 5 * 60 * 1e3;
1363
+ var OTLP_CLEANUP_BATCH_DELAY_MS = 100;
1352
1364
  var OBSERVER_DIRECTORY_LEASE = {
1353
1365
  stateFile: FML_OBSERVER_STATE_FILE,
1354
1366
  lockFile: FML_OBSERVER_LOCK_FILE
@@ -1452,6 +1464,9 @@ var hookQuarantineMaxAgeMs = parseHookQuarantineMaxAgeMs(
1452
1464
  var otlpSpoolMaxBytes = parseOtlpSpoolMaxBytes(
1453
1465
  process.env.FML_OTLP_SPOOL_MAX_BYTES
1454
1466
  );
1467
+ var otlpSpoolMaxAgeMs = parseOtlpSpoolMaxAgeMs(
1468
+ process.env.FML_OTLP_SPOOL_MAX_AGE_HOURS
1469
+ );
1455
1470
  ensureDirs();
1456
1471
  var isolationWarning = observerOnlyOverrideWarning();
1457
1472
  if (isolationWarning) console.warn(isolationWarning);
@@ -1486,6 +1501,7 @@ var fatalShutdownScheduled = false;
1486
1501
  var heartbeatTimer = null;
1487
1502
  var hookInboxDrainTimer = null;
1488
1503
  var hookInboxDrainPromise = null;
1504
+ var otlpPruneTimer = null;
1489
1505
  var hookQuarantinePruneTimer = null;
1490
1506
  var stateUpdateRetryTimer = null;
1491
1507
  var syncLoop = null;
@@ -1510,9 +1526,10 @@ try {
1510
1526
  // FML_STATE_DIR can be caller-managed. Keep SQLite payload files private
1511
1527
  // without changing permissions on the shared parent directory.
1512
1528
  restrictDatabaseDirectory: false,
1529
+ maxOtlpSpoolAgeMs: otlpSpoolMaxAgeMs,
1530
+ maxOtlpSpoolBytes: otlpSpoolMaxBytes,
1513
1531
  ...hookSpoolMaxBytes === void 0 ? {} : { maxHookSpoolBytes: hookSpoolMaxBytes },
1514
- ...hookQuarantineMaxAgeMs === void 0 ? {} : { maxHookQuarantineAgeMs: hookQuarantineMaxAgeMs },
1515
- ...otlpSpoolMaxBytes === void 0 ? {} : { maxOtlpSpoolBytes: otlpSpoolMaxBytes }
1532
+ ...hookQuarantineMaxAgeMs === void 0 ? {} : { maxHookQuarantineAgeMs: hookQuarantineMaxAgeMs }
1516
1533
  });
1517
1534
  } catch (error) {
1518
1535
  removeInitialLeasesBestEffort(initial.instanceId);
@@ -1546,7 +1563,7 @@ var hookRejectionStatus = () => ({
1546
1563
  ...lastRejectedReason !== void 0 ? { lastRejectedReason } : {},
1547
1564
  ...lastRejectedTarget !== void 0 ? { lastRejectedTarget } : {}
1548
1565
  });
1549
- var packageVersion = true ? "0.8.7+af45d2af" : "dev";
1566
+ var packageVersion = true ? "0.8.10-dev.1+09fae5b6" : "dev";
1550
1567
  var health = () => ({
1551
1568
  status: "ok",
1552
1569
  service: OBSERVER_SERVICE_NAME,
@@ -1611,6 +1628,7 @@ var shutdown = async () => {
1611
1628
  if (heartbeatTimer) clearInterval(heartbeatTimer);
1612
1629
  if (hookInboxDrainTimer) clearInterval(hookInboxDrainTimer);
1613
1630
  if (hookQuarantinePruneTimer) clearInterval(hookQuarantinePruneTimer);
1631
+ if (otlpPruneTimer) clearTimeout(otlpPruneTimer);
1614
1632
  if (stateUpdateRetryTimer) clearTimeout(stateUpdateRetryTimer);
1615
1633
  sessionEndSyncWakeScheduler.stop();
1616
1634
  autoUpdater?.stop();
@@ -1866,8 +1884,30 @@ try {
1866
1884
  } catch (error) {
1867
1885
  scheduleFatalShutdown(error);
1868
1886
  }
1869
- }, HOOK_QUARANTINE_PRUNE_INTERVAL_MS);
1887
+ }, SPOOL_CLEANUP_INTERVAL_MS);
1870
1888
  hookQuarantinePruneTimer.unref();
1889
+ let nextOtlpAgeCheckAtMs = 0;
1890
+ const pruneOtlp = () => {
1891
+ if (shuttingDown) return;
1892
+ try {
1893
+ const nowMs = Date.now();
1894
+ let removed = 0;
1895
+ if (nowMs >= nextOtlpAgeCheckAtMs) {
1896
+ removed = database.pruneExpiredOtlpEnvelopes(nowMs);
1897
+ if (removed < 100)
1898
+ nextOtlpAgeCheckAtMs = nowMs + SPOOL_CLEANUP_INTERVAL_MS;
1899
+ }
1900
+ if (removed === 0) removed = database.pruneOtlpCapacity(nowMs);
1901
+ otlpPruneTimer = setTimeout(
1902
+ pruneOtlp,
1903
+ removed > 0 ? OTLP_CLEANUP_BATCH_DELAY_MS : SPOOL_CLEANUP_INTERVAL_MS
1904
+ );
1905
+ otlpPruneTimer.unref();
1906
+ } catch (error) {
1907
+ scheduleFatalShutdown(error);
1908
+ }
1909
+ };
1910
+ pruneOtlp();
1871
1911
  hookSyncLoop = createHookSyncLoop({
1872
1912
  database,
1873
1913
  version: packageVersion