sporades 0.9.28 → 0.9.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGES.md CHANGED
@@ -2,172 +2,25 @@
2
2
 
3
3
  ## Unreleased - 2026-09-23
4
4
 
5
- Changes since v0.9.26.
6
-
7
- ### 🚀 Features
8
-
9
- - Verify prerender parity and track renderer dependencies in Dev (392b1bf2).
10
- - Expose deliberate prerender browser handover (daa88f83).
11
- - Complete ordered prerender placement and diagnostics (d5aa8bbe).
12
- - Emit one Vite prerender fragment (769f523d).
5
+ Changes since v0.9.28.
13
6
 
14
7
  ### 🐛 Bug Fixes
15
8
 
16
- - Retain linked package resolution alternatives (68c63e53).
17
- - Bound dependency polling and observe path accessors (93bca941).
18
- - Observe custom require.resolve search paths (cdfaacff).
19
- - Observe package self-reference export targets (0b32dd2b).
20
- - Share substitution observation for package imports (9536e562).
21
- - Observe resolution changes inside selected packages (9325e4e6).
22
- - Observe alternatives to successful renderer imports (6030a36d).
23
- - Keep declarative shadow hosts within fragment ownership (a4684e8e).
24
- - Reject inert reserved boundaries in final HTML (7fd93332).
25
- - Observe nearer manifestless renderer packages (fdced03b).
26
- - Bound and normalize unknown marker diagnostics (f695fea2).
27
- - Bound module-scope Annex B wrapper aliases (fe313576).
28
- - Normalize CommonJS renderer default exports (20d92eb4).
29
- - Track renderer tsconfig mappings and config inputs (51c875ed).
30
- - Separate parameter and body environments (da3772a5).
31
- - Reject synthetic CommonJS wrapper arguments (adbc8ba3).
32
- - Verify final Vite fragment ownership (ce8a333e).
33
- - Reject dynamic CommonJS with scope (9183e3c2).
34
- - Verify dismissal restores the authored DOM (9edefe18).
35
- - Preserve source import.meta through aliases (ea6f8a2c).
36
- - Diagnose malformed named marker directives (30083989).
37
- - Reject shadowed direct-eval call syntax (341c4a49).
38
- - Preserve author ancestors and implicit wrapper ownership (bb4733a5).
39
- - Watch successful package-resolution manifests (c8c7070d).
40
- - Use browser HTML parsing and reject direct CJS eval (75b44468).
41
- - Watch package-import alias manifests and targets (0ceff350).
42
- - Observe runtime module resolution attempts (2dd1181c).
43
- - Reject fragment document-root mutations (525fcd9c).
44
- - Finalize HTML before output-derived Vite assets (4cb4703c).
45
- - Detect reserved comments through HTML parsing (b0a753de).
46
- - Retain all root CommonJS wrapper redeclarations (42f0a7a8).
47
- - Watch missing computed package export roots (b512d596).
48
- - Initialize CommonJS source module locations (6631c412).
49
- - Place fragments after Vite composes deferred tags (95a5fdff).
50
- - Track failed local imports without duplicate rebuilds (122c92d8).
51
- - Preserve failure envelopes and diagnose empty configurations (389159b1).
52
- - Preserve module.require and TypeScript CommonJS semantics (0483d227).
53
- - Retain worker until private completion is consumed (6db56268).
54
- - Recover failed renderer builds after package installation (2924c02c).
55
- - Reject fragment-created ancestors spanning boundary ends (c2678825).
56
- - Isolate bootstrap completion from renderer progress messages (3a21a2e4).
57
- - Redact computed runtime dependency diagnostics (9b5c776e).
58
- - Track attempted CommonJS renderer dependencies before evaluation (f647e116).
59
- - Reject parser-moved fragment content before publication (688e3ca4).
60
- - Preserve quoted doctype identifiers during placement (efd3b86f).
61
- - Reject untrackable runtime require of ESM render dependencies (d09a8737).
62
- - Preserve foreign-content CDATA during placement (0aa70731).
63
- - Validate source boundaries and dismiss parsed DOM ranges (054aebb3).
64
- - Retain CommonJS wrapper values through var declarations (d1154712).
65
- - Protect prerender boundaries and regenerate public API docs (635070d8).
66
- - Preserve writable require and bound dynamic imports (9fdf7bf8).
67
- - Isolate prerender execution and writable module locations (d7e83717).
68
- - Merge renderer diagnostic aliases (07b0d17f).
69
- - Apply file URL alias boundaries (5c030678).
70
- - Redact project-root file URL equality (0f6545e8).
71
- - Map raw file URL diagnostic aliases (5eb2954d).
72
- - Bound raw file URL redaction (413ec06d).
73
- - Bound prerender diagnostic aliases (d290f59d).
74
- - Redact unresolved external file URLs (6516e18a).
75
- - Redact unresolved external renderer imports (8891bc7a).
76
- - Preserve nested prerender diagnostic paths (5f5e3fff).
77
- - Redact hoisted renderer runtime paths (b520367e).
78
- - Load plain hoisted prerender modules (4d762255).
79
- - Reject early prerender worker exit (f5aec1a1).
80
- - Classify ambiguous renderer modules (a10445fd).
81
- - Classify package-scoped CommonJS renderers (9c3dff53).
82
- - Preserve renderer hashbang and directives (95b5807c).
83
- - Preserve renderer class member names (29810c22).
84
- - Preserve CommonJS wrapper delete semantics (14da5e3e).
85
- - Preserve CommonJS wrapper assignments (86d06b58).
86
- - Resolve renderer lexical bindings accurately (e249665d).
87
- - Preserve static CommonJS renderer imports (513995da).
88
- - Preserve nested CTS module locations (ebf0db8f).
89
- - Treat noscript as raw prerender text (e5ed75f4).
90
- - Preserve nested CommonJS module locations (5920306e).
91
- - Track HTML attribute value states (aac17b2e).
92
- - Return honest renderer transform loaders (dc0f509a).
93
- - Tokenize less-than text safely (96fc6a42).
94
- - Fail closed on incomplete prerender HTML (b87c34ad).
95
- - Discover renderer tsconfig per module (930f3f64).
96
- - Reject Windows renderer module roots (f5400ef0).
97
- - Bound malformed prerender HTML scanning (9b25a1c7).
98
- - Preserve renderer tsconfig authority (c6b965e0).
99
- - Redact encoded project file URLs (0a184995).
100
- - Preserve renderer import meta URLs (03df87e1).
101
- - Replace only HTML prerender markers (5020a1f3).
102
- - Bound hostile renderer message access (17fc79ed).
103
- - Preserve HTML offsets during case folding (a6a1bbe8).
104
- - Contain renderer-owned error details (89f91139).
105
- - Locate prerender fallback body safely (78bf529c).
106
- - Redact prerender runtime project paths (556df1d4).
107
- - Execute prerender bundles without ESM cache growth (62506646).
108
-
109
- ### 🔧 Improvements
110
-
111
- - Expose internal renderer loader mapping (be8d5303).
112
-
113
- ### 📝 Documentation
114
-
115
- - Link canonical module reference and assert parity (fce2ad85).
116
- - Verify package-import manifest retargeting (91bb7996).
117
- - Complete canonical client API requirements (8690e560).
118
- - Specify prerender in canonical configuration reference (0fee4c1e).
119
- - Expose multipart limits and runtime bounds (#98) (ad023b9e).
9
+ - Evict only on an unanswered ping and document the heartbeat (66f964e0).
10
+ - Ping idle WebSockets so proxy idle timeouts do not strand pages (7ee9c7f8).
11
+ - Start no freshclam after retained-updater cleanup once shutdown began (0185fc01).
12
+ - Close ClamAV refresh shutdown race and recover crashed clamd (6c23b686).
13
+ - Start degraded on stale ClamAV signatures and allow 26h freshness (79dd3ece).
14
+ - Own ClamAV signature refresh in the Capsule runtime (17193be4).
120
15
 
121
16
  ### 🧪 Tests
122
17
 
123
- - Cover Hosted and Container warning envelopes (0e4bd01a).
124
- - Merge file URL alias collisions (d1a26a5e).
125
- - Redact bare project-root file URLs (6283b5d9).
126
- - Redact project-root file URL equality (848d96c9).
127
- - Redact contained and mixed-case file URLs (c242cc3b).
128
- - Preserve sibling file URL prefixes (5495bdfa).
129
- - Bound prerender diagnostic root aliases (fbb5c3b1).
130
- - Redact unresolved external file URLs (004832b6).
131
- - Redact unresolved external renderer imports (b7c7dabb).
132
- - Preserve nested prerender diagnostic context (4b42f056).
133
- - Redact hoisted prerender runtime paths (11b1d918).
134
- - Cover prerender namespace and worker outcomes (2936ae6e).
135
- - Fail closed when prerender worker exits (08a703cc).
136
- - Detect ambiguous renderer module formats (af1963c3).
137
- - Preserve renderer hashbang and directives (2008d12c).
138
- - Preserve non-computed renderer member names (a11d2822).
139
- - Preserve CommonJS wrapper delete semantics (9ef87b2e).
140
- - Preserve CommonJS wrapper write targets (1de31e18).
141
- - Preserve renderer lexical shadowing (9452a5cb).
142
- - Preserve static renderer requires and cache bounds (f572e4ff).
143
- - Preserve nested CTS renderer paths (3bb7d4a8).
144
- - Ignore prerender markers inside noscript (f7172af4).
145
- - Preserve nested CommonJS renderer paths (d40f2302).
146
- - Specify renderer output loader mapping (cdcd65b2).
147
- - Tokenize prose and HTML declarations safely (0e71c4e6).
148
- - Prove extended TSX renderer config (a4da6b3f).
149
- - Preserve less-than text during marker scanning (3481ad1e).
150
- - Reject unterminated prerender HTML tags (fdcc0c5f).
151
- - Discover extended renderer tsconfig (21bc1984).
152
- - Reject Windows renderer module paths (0f4ea77f).
153
- - Bound malformed HTML comment recovery (644acc13).
154
- - Preserve renderer tsconfig semantics (4d1e98a9).
155
- - Redact encoded renderer file URLs (59c22625).
156
- - Preserve transitive renderer import meta URL (fad9ea03).
157
- - Ignore prerender marker text in raw HTML (4d2948d4).
158
- - Contain hostile renderer message access (d74c1372).
159
- - Cover Unicode-safe prerender body offsets (77d2d1c4).
160
- - Reject renderer-owned structured errors (589ab6e9).
161
- - Cover HTML-aware prerender fallback placement (93a48e19).
162
- - Cover prerender runtime path redaction (5e2dadb4).
163
- - Cover prerender execution edge cases (6667a916).
164
- - Specify one Vite prerender fragment (69ea0c58).
18
+ - Buffer raw WebSocket frames across TCP chunks (e07288dd).
19
+ - Describe the ClamAV freshness gate without a fixed age (b180ac1a).
165
20
 
166
21
  ### 📦 Packaging
167
22
 
168
- - Install local deps (fd0cf1d6).
169
- - Include regenerated prerender declaration map (a655d0fc).
170
- - Specialize package-scoped CommonJS helpers (de79f9ba).
23
+ - Regenerate runtime artifacts for the WebSocket heartbeat (d0b1193b).
171
24
 
172
25
  ## v0.9.11 - 2026-09-02
173
26
 
@@ -269,3 +122,4 @@ generated artifacts, and documentation.
269
122
 
270
123
 
271
124
 
125
+
@@ -42692,6 +42692,10 @@ var bash52CommandVocabulary = Object.freeze([
42692
42692
  "}"
42693
42693
  ]);
42694
42694
  var bashCommandNames = new Set(bash52CommandVocabulary);
42695
+ var clamavSignatureMaxAgeMs = 26 * 60 * 60 * 1e3;
42696
+ var clamavRefreshIntervalMs = 60 * 60 * 1e3;
42697
+ var clamavRefreshRetryMs = 15 * 60 * 1e3;
42698
+ var clamavRefreshTimeoutMs = 5 * 60 * 1e3;
42695
42699
 
42696
42700
  // src/teams-runtime.ts
42697
42701
  var TEAM_JOIN_LINK_DEFAULT_TTL_SECONDS = 60 * 60 * 24;
@@ -42985,7 +42989,7 @@ var MutationExecutionStorage = process.getBuiltinModule("node:async_hooks").Asyn
42985
42989
  var mutationExecution = new MutationExecutionStorage();
42986
42990
 
42987
42991
  // src/cli/cli-version.ts
42988
- var CLI_VERSION = "0.9.28";
42992
+ var CLI_VERSION = "0.9.29";
42989
42993
 
42990
42994
  // src/cli/schedule-inspection-envelope.ts
42991
42995
  var SCHEDULE_DIAGNOSTIC_FIELDS = /* @__PURE__ */ new Set([
package/bin/sporades.js CHANGED
@@ -106223,9 +106223,10 @@ async function contentPolicyOutcome(row, bytes) {
106223
106223
  if (bytes.subarray(0, 5).toString("ascii") === "%PDF-") return await validatePdfIngress(bytes) && /\.pdf$/.test(name2) && type === "application/pdf" ? "clean" : "rejected";
106224
106224
  return safeUntrustedText(bytes) && /\.txt$/.test(name2) && type === "text/plain" ? "clean" : "rejected";
106225
106225
  }
106226
+ var clamavSignatureMaxAgeMs = 26 * 60 * 60 * 1e3;
106226
106227
  function isCurrentClamavSignature(signature, now2 = Date.now()) {
106227
106228
  const builtAt = Date.parse(signature?.updatedAt);
106228
- return Boolean(signature) && typeof signature?.version === "string" && /^daily:\d{1,12}$/.test(signature.version) && Number.isFinite(builtAt) && builtAt <= now2 && now2 - builtAt <= 24 * 60 * 60 * 1e3;
106229
+ return Boolean(signature) && typeof signature?.version === "string" && /^daily:\d{1,12}$/.test(signature.version) && Number.isFinite(builtAt) && builtAt <= now2 && now2 - builtAt <= clamavSignatureMaxAgeMs;
106229
106230
  }
106230
106231
  function collectBoundedToolOutput(child, timeoutMs, maximumBytes = 8192) {
106231
106232
  return new Promise((resolve) => {
@@ -106529,6 +106530,108 @@ async function stopOwnedClamavChildren(database) {
106529
106530
  });
106530
106531
  if (failures.length) throw new AggregateError(failures, "ClamAV child cleanup failed.");
106531
106532
  }
106533
+ var clamavBootRetryDelaysMs = [5e3, 15e3];
106534
+ var clamavRefreshIntervalMs = 60 * 60 * 1e3;
106535
+ var clamavRefreshRetryMs = 15 * 60 * 1e3;
106536
+ var clamavRefreshTimeoutMs = 5 * 60 * 1e3;
106537
+ function clamavSpawn(database, command, args) {
106538
+ return database.__clamavTest?.spawn ? database.__clamavTest.spawn(command, args) : childProcess.spawn(command, args, { stdio: "ignore" });
106539
+ }
106540
+ function clamavSchedule(database, callback, delayMs) {
106541
+ if (database.__clamavTest?.schedule) return database.__clamavTest.schedule(callback, delayMs);
106542
+ const timer = setTimeout(callback, delayMs);
106543
+ timer.unref?.();
106544
+ return () => clearTimeout(timer);
106545
+ }
106546
+ async function runFreshclam(database, deadline) {
106547
+ const retained = database.__clamavUpdateProcess;
106548
+ if (retained) {
106549
+ await terminateChild(retained, clamavTerminateTimeout(database), database);
106550
+ unobserveClamavChild(database, retained);
106551
+ if (database.__clamavUpdateProcess === retained) database.__clamavUpdateProcess = null;
106552
+ if (database.__clamavRefreshStopped) return false;
106553
+ }
106554
+ if (clamavRemaining(database, deadline) <= 0) return false;
106555
+ const update = clamavSpawn(database, "/usr/bin/freshclam", ["--config-file=/etc/clamav/freshclam.conf"]);
106556
+ database.__clamavUpdateProcess = update;
106557
+ const completed = await waitForChild(update, clamavRemaining(database, deadline));
106558
+ if (!completed) await terminateChild(update, Math.min(clamavTerminateTimeout(database), clamavRemaining(database, deadline)), database);
106559
+ if (database.__clamavUpdateProcess === update) database.__clamavUpdateProcess = null;
106560
+ return completed;
106561
+ }
106562
+ function nextClamavRefreshDelay(database, signature, refreshed) {
106563
+ if (!refreshed) return clamavRefreshRetryMs;
106564
+ const expiresIn = Date.parse(signature?.updatedAt) + clamavSignatureMaxAgeMs - clamavNow(database);
106565
+ return Math.max(1e3, Math.min(clamavRefreshIntervalMs, expiresIn + 1e3));
106566
+ }
106567
+ async function startClamd(database, deadline) {
106568
+ if (clamavRemaining(database, deadline) <= 0) return false;
106569
+ const daemon = clamavSpawn(database, "/usr/sbin/clamd", ["--foreground", "--config-file=/etc/clamav/clamd.conf"]);
106570
+ database.__clamavProcess = daemon;
106571
+ observeClamavChild(database, daemon);
106572
+ if (await waitForClamavReadiness(database, daemon, deadline, "/tmp/sporades-clamd.sock")) {
106573
+ database.clamavReady = true;
106574
+ return true;
106575
+ }
106576
+ await stopOwnedClamavChildren(database);
106577
+ return false;
106578
+ }
106579
+ async function refreshClamavSignatures(database) {
106580
+ const deadline = clamavNow(database) + clamavRefreshTimeoutMs;
106581
+ const refreshed = await runFreshclam(database, deadline);
106582
+ if (database.__clamavRefreshStopped) return clamavRefreshRetryMs;
106583
+ const signature = await verifiedClamavSignature(database, deadline);
106584
+ if (database.__clamavRefreshStopped) return clamavRefreshRetryMs;
106585
+ if (!isCurrentClamavSignature(signature, clamavNow(database))) {
106586
+ database.clamavReady = false;
106587
+ await emitClamavRefreshWarning(database, "CLAMAV_SIGNATURE_STALE_AFTER_REFRESH");
106588
+ return clamavRefreshRetryMs;
106589
+ }
106590
+ const crashed = database.__clamavProcess;
106591
+ if (crashed && clamavChildTerminated(crashed)) {
106592
+ unobserveClamavChild(database, crashed);
106593
+ database.__clamavProcess = null;
106594
+ await emitClamavRefreshWarning(database, "CLAMAV_DAEMON_EXITED");
106595
+ if (database.__clamavRefreshStopped) return clamavRefreshRetryMs;
106596
+ }
106597
+ if (!database.__clamavProcess) {
106598
+ const started = await startClamd(database, clamavNow(database) + (database.__clamavTest?.startupTimeoutMs ?? 12e4));
106599
+ if (database.__clamavRefreshStopped) return clamavRefreshRetryMs;
106600
+ if (!started) {
106601
+ await emitClamavRefreshWarning(database, "CLAMAV_DAEMON_START_FAILED");
106602
+ return clamavRefreshRetryMs;
106603
+ }
106604
+ return nextClamavRefreshDelay(database, signature, refreshed);
106605
+ }
106606
+ if (await loadedClamavSignatureVersion(database) !== signature.version) await clamavSocketCommand(database, Buffer.from("zRELOAD\0"), 64);
106607
+ if (!refreshed) await emitClamavRefreshWarning(database, "CLAMAV_SIGNATURE_REFRESH_FAILED");
106608
+ return nextClamavRefreshDelay(database, signature, refreshed);
106609
+ }
106610
+ async function emitClamavRefreshWarning(database, code) {
106611
+ try {
106612
+ await database.log?.emit?.({ category: "platform", event: "file.inspection.signature-refresh-failed", level: "warn", message: "ClamAV signature refresh did not leave file inspection ready", data: { schema: "v1", outcome: "failed", code } });
106613
+ } catch {
106614
+ }
106615
+ }
106616
+ function scheduleClamavRefresh(database, delayMs) {
106617
+ if (database.__clamavRefreshStopped) return;
106618
+ database.__clamavRefreshCancel = clamavSchedule(database, () => {
106619
+ database.__clamavRefreshCancel = null;
106620
+ const pending = refreshClamavSignatures(database).catch(async () => {
106621
+ await emitClamavRefreshWarning(database, "CLAMAV_SIGNATURE_REFRESH_FAILED");
106622
+ return clamavRefreshRetryMs;
106623
+ }).then((nextDelayMs) => {
106624
+ if (database.__clamavRefreshPending === pending) database.__clamavRefreshPending = null;
106625
+ scheduleClamavRefresh(database, nextDelayMs);
106626
+ });
106627
+ database.__clamavRefreshPending = pending;
106628
+ }, delayMs);
106629
+ }
106630
+ function stopClamavRefresh(database) {
106631
+ database.__clamavRefreshStopped = true;
106632
+ database.__clamavRefreshCancel?.();
106633
+ database.__clamavRefreshCancel = null;
106634
+ }
106532
106635
  async function initializeClamavRuntime(database) {
106533
106636
  const required = database.endpoints?.some((endpoint) => endpoint?.options?.body?.multipart?.inspection?.requiredInspectors?.includes("clamav"));
106534
106637
  database.clamavRequired = Boolean(required);
@@ -106540,43 +106643,46 @@ async function initializeClamavRuntime(database) {
106540
106643
  database.clamavReady = await waitForClamavReadiness(database, database.__clamavDevSidecar.process, deadline, database.__clamavDevSidecar.socketPath);
106541
106644
  return database.clamavReady;
106542
106645
  }
106543
- if (database.__clamavTest) {
106646
+ if (database.__clamavTest && !database.__clamavTest.managed) {
106544
106647
  database.clamavReady = Boolean(await currentLoadedClamavSignature(database, deadline));
106545
106648
  return database.clamavReady;
106546
106649
  }
106547
- if (process.env.SPORADES_CLAMAV_MANAGED !== "1") return false;
106548
- try {
106650
+ if (!database.__clamavTest && process.env.SPORADES_CLAMAV_MANAGED !== "1") return false;
106651
+ if (!database.__clamavTest) try {
106549
106652
  fs.mkdirSync("/app/data/clamav", { recursive: true });
106550
106653
  fs.mkdirSync("/tmp/sporades-clamav", { recursive: true });
106551
106654
  } catch {
106552
106655
  return false;
106553
106656
  }
106554
- if (clamavRemaining(database, deadline) <= 0) return false;
106555
- const update = childProcess.spawn("/usr/bin/freshclam", ["--config-file=/etc/clamav/freshclam.conf"], { stdio: "ignore" });
106556
- database.__clamavUpdateProcess = update;
106557
- const updateCompleted = await waitForChild(update, clamavRemaining(database, deadline));
106558
- if (!updateCompleted) await terminateChild(update, Math.min(clamavTerminateTimeout(database), clamavRemaining(database, deadline)), database);
106559
- database.__clamavUpdateProcess = null;
106560
- const signature = await verifiedClamavSignature(database, deadline);
106561
- if (!isCurrentClamavSignature(signature, clamavNow(database))) return false;
106562
- if (clamavRemaining(database, deadline) <= 0) return false;
106563
- const daemon = childProcess.spawn("/usr/sbin/clamd", ["--foreground", "--config-file=/etc/clamav/clamd.conf"], { stdio: "ignore" });
106564
- database.__clamavProcess = daemon;
106565
- observeClamavChild(database, daemon);
106566
- if (await waitForClamavReadiness(database, daemon, deadline, "/tmp/sporades-clamd.sock")) {
106567
- database.clamavReady = true;
106568
- const updater = childProcess.spawn("/usr/bin/freshclam", ["--daemon", "--foreground=true", "--config-file=/etc/clamav/freshclam.conf"], { stdio: "ignore" });
106569
- database.__clamavUpdateProcess = updater;
106570
- observeClamavChild(database, updater);
106571
- return true;
106657
+ database.__clamavRefreshStopped = false;
106658
+ let refreshed = false;
106659
+ let signature = null;
106660
+ for (let attempt = 0; ; attempt += 1) {
106661
+ refreshed = await runFreshclam(database, deadline);
106662
+ signature = await verifiedClamavSignature(database, deadline);
106663
+ if (isCurrentClamavSignature(signature, clamavNow(database))) break;
106664
+ const delayMs = clamavBootRetryDelaysMs[attempt];
106665
+ if (delayMs === void 0 || clamavRemaining(database, deadline) <= delayMs) {
106666
+ database.clamavReady = false;
106667
+ await emitClamavRefreshWarning(database, "CLAMAV_STARTUP_DEGRADED");
106668
+ scheduleClamavRefresh(database, clamavRefreshRetryMs);
106669
+ return true;
106670
+ }
106671
+ await clamavDelay(database, delayMs);
106572
106672
  }
106573
- await stopOwnedClamavChildren(database);
106574
- return false;
106673
+ if (!await startClamd(database, deadline)) return false;
106674
+ scheduleClamavRefresh(database, nextClamavRefreshDelay(database, signature, refreshed));
106675
+ return true;
106575
106676
  }
106576
106677
  async function shutdownClamavRuntime(database) {
106577
106678
  database.clamavReady = false;
106578
- if (!database.__clamavDevSidecar?.externallyManaged) await stopOwnedClamavChildren(database);
106579
- else {
106679
+ stopClamavRefresh(database);
106680
+ if (!database.__clamavDevSidecar?.externallyManaged) {
106681
+ await stopOwnedClamavChildren(database);
106682
+ await database.__clamavRefreshPending;
106683
+ if (database.__clamavProcess || database.__clamavUpdateProcess) await stopOwnedClamavChildren(database);
106684
+ database.clamavReady = false;
106685
+ } else {
106580
106686
  unobserveClamavChild(database, database.__clamavDevSidecar.process);
106581
106687
  database.__clamavProcess = null;
106582
106688
  database.__clamavUpdateProcess = null;
@@ -106584,7 +106690,7 @@ async function shutdownClamavRuntime(database) {
106584
106690
  }
106585
106691
  async function checkClamavRuntime(database) {
106586
106692
  if (!database.clamavRequired) return { ok: true };
106587
- const children = [database.__clamavDevSidecar?.process, database.__clamavProcess, database.__clamavUpdateProcess];
106693
+ const children = [database.__clamavDevSidecar?.process, database.__clamavProcess];
106588
106694
  for (const child of children) observeClamavChild(database, child);
106589
106695
  if (children.some(clamavChildTerminated)) {
106590
106696
  database.clamavReady = false;
@@ -122065,7 +122171,9 @@ async function runClientAccessKeyOperation(database, auth, message, sessionToken
122065
122171
  };
122066
122172
  }
122067
122173
  }
122068
- function createWebSocketHub(getDatabase, trustedRefresh = null) {
122174
+ var WEBSOCKET_HEARTBEAT_MS = 3e4;
122175
+ function createWebSocketHub(getDatabase, trustedRefresh = null, options = {}) {
122176
+ const heartbeatMs = options.heartbeatMs ?? WEBSOCKET_HEARTBEAT_MS;
122069
122177
  const clients = /* @__PURE__ */ new Set();
122070
122178
  const journeys = /* @__PURE__ */ new Map();
122071
122179
  const connectionTokens = /* @__PURE__ */ new Map();
@@ -122134,15 +122242,33 @@ function createWebSocketHub(getDatabase, trustedRefresh = null) {
122134
122242
  connectedAt: now2,
122135
122243
  lastSeenAt: now2,
122136
122244
  journey: null,
122137
- journeySubscriptions: /* @__PURE__ */ new Set()
122245
+ journeySubscriptions: /* @__PURE__ */ new Set(),
122246
+ lastFrameAt: Date.now(),
122247
+ pingSentAt: null,
122248
+ heartbeat: null
122138
122249
  };
122139
122250
  clients.add(client);
122251
+ const unanswered = () => client.pingSentAt !== null && client.lastFrameAt < client.pingSentAt;
122252
+ client.heartbeat = setInterval(() => {
122253
+ if (client.closing || socket.destroyed) return;
122254
+ if (unanswered()) {
122255
+ setImmediate(() => {
122256
+ if (unanswered()) socket.destroy();
122257
+ });
122258
+ return;
122259
+ }
122260
+ client.pingSentAt = Date.now();
122261
+ socket.write(Buffer.from([137, 0]));
122262
+ }, heartbeatMs);
122263
+ client.heartbeat.unref?.();
122140
122264
  socket.on("data", (chunk) => {
122265
+ client.lastFrameAt = Date.now();
122141
122266
  client.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
122142
122267
  client.buffer = Buffer.concat([client.buffer, chunk]);
122143
122268
  drainWebSocketFrames(client, (message) => enqueueClientMessage(client, message));
122144
122269
  });
122145
122270
  const removeClient = () => {
122271
+ clearInterval(client.heartbeat);
122146
122272
  clients.delete(client);
122147
122273
  trustedRefresh?.disconnected(client.id);
122148
122274
  client.subscriptions.clear();
@@ -123265,6 +123391,16 @@ function drainWebSocketFrames(client, onMessage) {
123265
123391
  closeWebSocketClient(client);
123266
123392
  return;
123267
123393
  }
123394
+ if (opcode === 9) {
123395
+ const pong = Buffer.alloc(payload.length);
123396
+ for (let index = 0; index < payload.length; index += 1) {
123397
+ pong[index] = mask ? payload[index] ^ mask[index % 4] : payload[index];
123398
+ }
123399
+ if (!client.closing && !client.socket.destroyed && pong.length < 126) {
123400
+ client.socket.write(Buffer.concat([Buffer.from([138, pong.length]), pong]));
123401
+ }
123402
+ continue;
123403
+ }
123268
123404
  if (opcode !== 1) {
123269
123405
  continue;
123270
123406
  }
@@ -123280,6 +123416,7 @@ function closeWebSocketClient(client) {
123280
123416
  return;
123281
123417
  }
123282
123418
  client.closing = true;
123419
+ clearInterval(client.heartbeat);
123283
123420
  try {
123284
123421
  client.socket.write(Buffer.from([136, 0]), () => {
123285
123422
  client.socket.end();
@@ -131112,7 +131249,7 @@ jobs:
131112
131249
  }
131113
131250
 
131114
131251
  // src/cli/cli-version.ts
131115
- var CLI_VERSION = "0.9.28";
131252
+ var CLI_VERSION = "0.9.29";
131116
131253
 
131117
131254
  // src/cli/sporades.ts
131118
131255
  var SUPPORTED_TEMPLATES = new Set(CLIENT_TEMPLATES);
@@ -1,2 +1,2 @@
1
- export declare const CLI_VERSION = "0.9.28";
1
+ export declare const CLI_VERSION = "0.9.29";
2
2
  //# sourceMappingURL=cli-version.d.ts.map
@@ -1,3 +1,3 @@
1
1
  // Generated by `npm run build` from package.json. Do not edit by hand.
2
- export const CLI_VERSION = "0.9.28";
2
+ export const CLI_VERSION = "0.9.29";
3
3
  //# sourceMappingURL=cli-version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-ingress-runtime.d.ts","sourceRoot":"","sources":["../src/file-ingress-runtime.ts"],"names":[],"mappings":"AAWA,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAWtC,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,MAAM,WAAoN;AAgrBpR,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,oBAoF/E;AAoCD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,WAW5D;AAmCD,wBAAgB,mCAAmC,CAAC,IAAI,EAAE,MAAM,WAE/D;AACD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,WAwB5D;AAMD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,WAmBxD;AAaD,eAAO,MAAM,uBAAuB,mBAElC,CAAC;AAgOH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,WAgDvD;AAmBD,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,UAAU,GAAG,IAAI,EAAE,GAAG,SAAa,WAAgQ;AACvV,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,SAAO;QAA6B,OAAO;YAAU,MAAM;GAAy2B;AA4Hx/B,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,oBASnH;AAOD,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,UAAU,gBAkBjE;AACD,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,UAAU,iBAAuR;AACvV,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,UAAU;;GAA+jB;AAyE5nB,wBAAuB,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,UAAU,UAAO;;;kBAyCrO;AAED,sHAAsH;AACtH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,iBAAiB,CAAC,EAAE,UAAU,EAAE,UAAU,UAAO;;;;;;;;;;;;;;;;;;;GA4FtM;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,UAAU,cAiBhE;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU;iBAU9G,UAAU,WAAW,UAAU;;;;;;;;;sBAyD1B,UAAU;;;;6BASH,MAAM,WAAW,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBzD;AAED,wGAAwG;AACxG,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,QAEpF;AAED,6FAA6F;AAC7F,wBAAsB,8BAA8B,CAAC,QAAQ,EAAE,UAAU,oBAQxE;AAED,4EAA4E;AAC5E,wBAAsB,4BAA4B,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,GAAE,UAAe,iBAsChG;AAwCD,wGAAwG;AACxG,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,GAAE,UAAe;;;;;;;;;;IAuB3F"}
1
+ {"version":3,"file":"file-ingress-runtime.d.ts","sourceRoot":"","sources":["../src/file-ingress-runtime.ts"],"names":[],"mappings":"AAWA,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAWtC,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,MAAM,WAAoN;AAgrBpR,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,oBAoF/E;AAoCD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,WAW5D;AAmCD,wBAAgB,mCAAmC,CAAC,IAAI,EAAE,MAAM,WAE/D;AACD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,WAwB5D;AAMD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,WAmBxD;AAaD,eAAO,MAAM,uBAAuB,mBAElC,CAAC;AAgOH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,WAgDvD;AAsBD,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,UAAU,GAAG,IAAI,EAAE,GAAG,SAAa,WAAoQ;AAC3V,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,SAAO;QAA6B,OAAO;YAAU,MAAM;GAAy2B;AA4Hx/B,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,oBASnH;AA4DD,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,UAAU,gBAuBjE;AACD,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,UAAU,iBAAse;AACtiB,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,UAAU;;GAA+hB;AAyE5lB,wBAAuB,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,UAAU,UAAO;;;kBAyCrO;AAED,sHAAsH;AACtH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,iBAAiB,CAAC,EAAE,UAAU,EAAE,UAAU,UAAO;;;;;;;;;;;;;;;;;;;GA4FtM;AAED,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,UAAU,cAiBhE;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU;iBAU9G,UAAU,WAAW,UAAU;;;;;;;;;sBAyD1B,UAAU;;;;6BASH,MAAM,WAAW,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBzD;AAED,wGAAwG;AACxG,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,QAEpF;AAED,6FAA6F;AAC7F,wBAAsB,8BAA8B,CAAC,QAAQ,EAAE,UAAU,oBAQxE;AAED,4EAA4E;AAC5E,wBAAsB,4BAA4B,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,GAAE,UAAe,iBAsChG;AAwCD,wGAAwG;AACxG,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,GAAE,UAAe;;;;;;;;;;IAuB3F"}