@tradejs/cli 2.0.6 → 2.0.8

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.
@@ -0,0 +1,255 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/lib/runtimeLineage.ts
31
+ var runtimeLineage_exports = {};
32
+ __export(runtimeLineage_exports, {
33
+ buildRuntimeLineage: () => buildRuntimeLineage,
34
+ fingerprintRuntimeValue: () => fingerprintRuntimeValue,
35
+ resetRuntimeLineageCachesForTests: () => resetRuntimeLineageCachesForTests,
36
+ runtimeLineageKey: () => runtimeLineageKey,
37
+ runtimeLineagesMatch: () => runtimeLineagesMatch
38
+ });
39
+ module.exports = __toCommonJS(runtimeLineage_exports);
40
+ var import_node_child_process = require("child_process");
41
+ var import_node_crypto = require("crypto");
42
+ var import_promises = __toESM(require("fs/promises"));
43
+ var import_node_path = __toESM(require("path"));
44
+ var RUNTIME_CONTEXT_ENV_KEYS = [
45
+ "AI_MODE",
46
+ "MIN_AI_QUALITY",
47
+ "INTERVAL",
48
+ "DERIVATIVES_CONTEXT_ENABLED",
49
+ "DERIVATIVES_CONTEXT_TARGET_ENABLED",
50
+ "DERIVATIVES_CONTEXT_LOOKBACK_HOURS",
51
+ "DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS",
52
+ "DERIVATIVES_CONTEXT_EXCHANGE_PRIORITY",
53
+ "COINMARKETCAP_CONTEXT_ENABLED",
54
+ "COINMARKETCAP_CONTEXT_MAX_AGE_MS",
55
+ "COINMARKETCAP_CONTEXT_EXCHANGE_LIQUIDITY_ENABLED",
56
+ "COINMARKETCAP_CONTEXT_EXCHANGE_SLUGS",
57
+ "COINMARKETCAP_CONTEXT_FEAR_GREED_ENABLED",
58
+ "COINMARKETCAP_CONTEXT_HISTORICAL_ACCESS_MONTHS",
59
+ "COINMARKETCAP_CONTEXT_BACKFILL_ENABLED",
60
+ "COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
61
+ "COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
62
+ ];
63
+ var RUNTIME_CONTEXT_DATA_MODEL = {
64
+ derivativesSourceIntervals: "15m",
65
+ derivativesDerivedIntervals: "1h",
66
+ derivativesHourlyFallback: "stored-1h",
67
+ derivativesDataModelVersion: 2,
68
+ coinMarketCapAvailabilityModel: "closed-interval-all-daily",
69
+ coinMarketCapDataModelVersion: 3,
70
+ binanceBreadthUniverseModel: "fixed-versioned-top5-top10-top30-top50-top100",
71
+ binanceBreadthDataModelVersion: 2
72
+ };
73
+ var normalizeForStableJson = (value) => {
74
+ if (Array.isArray(value)) {
75
+ return value.map(normalizeForStableJson);
76
+ }
77
+ if (value && typeof value === "object") {
78
+ return Object.fromEntries(
79
+ Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalizeForStableJson(entry)])
80
+ );
81
+ }
82
+ return value;
83
+ };
84
+ var fingerprintRuntimeValue = (value) => (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
85
+ var gitLineageCache = /* @__PURE__ */ new Map();
86
+ var gateFingerprintCache = /* @__PURE__ */ new Map();
87
+ var binanceBreadthFingerprintCache = /* @__PURE__ */ new Map();
88
+ var resolveGitLineage = (projectRoot) => {
89
+ const cached = gitLineageCache.get(projectRoot);
90
+ if (cached) {
91
+ return cached;
92
+ }
93
+ const envSha = String(process.env.TRADEJS_GIT_SHA ?? "").trim();
94
+ if (envSha && envSha !== "unknown") {
95
+ const result = {
96
+ gitSha: envSha,
97
+ gitDirty: false
98
+ };
99
+ gitLineageCache.set(projectRoot, result);
100
+ return result;
101
+ }
102
+ try {
103
+ const gitSha = (0, import_node_child_process.execFileSync)("git", ["rev-parse", "HEAD"], {
104
+ cwd: projectRoot,
105
+ encoding: "utf8",
106
+ stdio: ["ignore", "pipe", "ignore"]
107
+ }).trim();
108
+ const status = (0, import_node_child_process.execFileSync)(
109
+ "git",
110
+ ["status", "--porcelain", "--untracked-files=normal"],
111
+ {
112
+ cwd: projectRoot,
113
+ encoding: "utf8",
114
+ stdio: ["ignore", "pipe", "ignore"]
115
+ }
116
+ ).trim();
117
+ const result = {
118
+ gitSha: gitSha || null,
119
+ gitDirty: status.length > 0
120
+ };
121
+ gitLineageCache.set(projectRoot, result);
122
+ return result;
123
+ } catch {
124
+ const result = {
125
+ gitSha: null,
126
+ gitDirty: null
127
+ };
128
+ gitLineageCache.set(projectRoot, result);
129
+ return result;
130
+ }
131
+ };
132
+ var readOptionalFile = async (filePath) => {
133
+ try {
134
+ return await import_promises.default.readFile(filePath);
135
+ } catch {
136
+ return null;
137
+ }
138
+ };
139
+ var resolveBinanceBreadthFingerprint = async (projectRoot) => {
140
+ if (binanceBreadthFingerprintCache.has(projectRoot)) {
141
+ return binanceBreadthFingerprintCache.get(projectRoot) ?? null;
142
+ }
143
+ const content = await readOptionalFile(
144
+ import_node_path.default.join(
145
+ projectRoot,
146
+ "packages/node/src/config/binanceBreadthUniverses.json"
147
+ )
148
+ );
149
+ let result = null;
150
+ if (content) {
151
+ try {
152
+ const parsed = JSON.parse(content.toString("utf8"));
153
+ result = typeof parsed.fingerprint === "string" && parsed.fingerprint.trim() ? parsed.fingerprint.trim() : null;
154
+ } catch {
155
+ result = null;
156
+ }
157
+ }
158
+ binanceBreadthFingerprintCache.set(projectRoot, result);
159
+ return result;
160
+ };
161
+ var resolveGateFingerprint = async ({
162
+ projectRoot,
163
+ strategyName,
164
+ gitSha
165
+ }) => {
166
+ const cacheKey = `${projectRoot}:${strategyName}:${gitSha ?? "unknown"}`;
167
+ const cached = gateFingerprintCache.get(cacheKey);
168
+ if (cached) {
169
+ return cached;
170
+ }
171
+ const relativeCandidates = [
172
+ `packages/strategies/src/${strategyName}/adapters/ai.ts`,
173
+ `packages/strategies/src/${strategyName}/guardrails.ts`,
174
+ `packages/strategies/src/${strategyName}/pockets.ts`,
175
+ `packages/strategies/src/${strategyName}/config.ts`,
176
+ "packages/node/src/ai.ts"
177
+ ];
178
+ const optionalEntries = await Promise.all(
179
+ relativeCandidates.map(async (relativePath) => {
180
+ const content = await readOptionalFile(
181
+ import_node_path.default.join(projectRoot, relativePath)
182
+ );
183
+ return content == null ? null : { relativePath, content };
184
+ })
185
+ );
186
+ const sourceEntries = [];
187
+ for (const entry of optionalEntries) {
188
+ if (entry != null) {
189
+ sourceEntries.push(entry);
190
+ }
191
+ }
192
+ const hash = (0, import_node_crypto.createHash)("sha256");
193
+ hash.update(strategyName);
194
+ if (sourceEntries.length) {
195
+ for (const entry of sourceEntries) {
196
+ hash.update(entry.relativePath);
197
+ hash.update(entry.content);
198
+ }
199
+ } else {
200
+ hash.update(gitSha ?? "unknown-git-sha");
201
+ }
202
+ const fingerprint = hash.digest("hex").slice(0, 16);
203
+ gateFingerprintCache.set(cacheKey, fingerprint);
204
+ return fingerprint;
205
+ };
206
+ var buildRuntimeLineage = async ({
207
+ projectRoot,
208
+ strategyName,
209
+ config,
210
+ runContext,
211
+ env = process.env
212
+ }) => {
213
+ const git = resolveGitLineage(projectRoot);
214
+ const context = {
215
+ ...Object.fromEntries(
216
+ RUNTIME_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
217
+ ),
218
+ ...RUNTIME_CONTEXT_DATA_MODEL,
219
+ binanceBreadthUniverseFingerprint: await resolveBinanceBreadthFingerprint(projectRoot),
220
+ ...runContext
221
+ };
222
+ return {
223
+ schemaVersion: 1,
224
+ ...git,
225
+ gateFingerprint: await resolveGateFingerprint({
226
+ projectRoot,
227
+ strategyName,
228
+ gitSha: git.gitSha
229
+ }),
230
+ configFingerprint: fingerprintRuntimeValue(config),
231
+ contextFingerprint: fingerprintRuntimeValue(context)
232
+ };
233
+ };
234
+ var runtimeLineageKey = (lineage) => [
235
+ lineage.schemaVersion,
236
+ lineage.gitSha ?? "unknown",
237
+ lineage.gitDirty == null ? "unknown" : lineage.gitDirty ? "dirty" : "clean",
238
+ lineage.gateFingerprint,
239
+ lineage.configFingerprint,
240
+ lineage.contextFingerprint
241
+ ].join(":");
242
+ var runtimeLineagesMatch = (left, right) => left != null && right != null && runtimeLineageKey(left) === runtimeLineageKey(right);
243
+ var resetRuntimeLineageCachesForTests = () => {
244
+ gitLineageCache.clear();
245
+ gateFingerprintCache.clear();
246
+ binanceBreadthFingerprintCache.clear();
247
+ };
248
+ // Annotate the CommonJS export names for ESM import in node:
249
+ 0 && (module.exports = {
250
+ buildRuntimeLineage,
251
+ fingerprintRuntimeValue,
252
+ resetRuntimeLineageCachesForTests,
253
+ runtimeLineageKey,
254
+ runtimeLineagesMatch
255
+ });
@@ -639,6 +639,7 @@ var buildReplayRuntimeComparisonDetails = ({
639
639
  ),
640
640
  limit
641
641
  ),
642
+ orderFailed: [],
642
643
  runtimeOnly: cappedRuntimeOnlyDetails,
643
644
  backtestOnly: cappedBacktestOnlyDetails,
644
645
  nearestCandidates: cappedNearestCandidates,
@@ -658,6 +659,7 @@ var buildReplayRuntimeComparisonDetails = ({
658
659
  };
659
660
  var buildReplayExchangeComparisonDetails = ({
660
661
  matched,
662
+ orderFailed = [],
661
663
  exchangeOnly,
662
664
  backtestOnly,
663
665
  exchangeEntries,
@@ -708,7 +710,7 @@ var buildReplayExchangeComparisonDetails = ({
708
710
  limit
709
711
  );
710
712
  return {
711
- capped: matched.length > limit || exchangeOnlyDetails.length > limit || backtestOnlyDetails.length > limit || nearestCandidates.length > limit,
713
+ capped: matched.length > limit || orderFailed.length > limit || exchangeOnlyDetails.length > limit || backtestOnlyDetails.length > limit || nearestCandidates.length > limit,
712
714
  limit,
713
715
  matched: capReplayDetails(
714
716
  matched.map(
@@ -724,6 +726,27 @@ var buildReplayExchangeComparisonDetails = ({
724
726
  ),
725
727
  limit
726
728
  ),
729
+ orderFailed: capReplayDetails(
730
+ orderFailed.map((item) => {
731
+ const detail = buildMatchedReplayDetail({
732
+ runtime: toExchangeDetail(item.exchange),
733
+ backtest: toBacktestParityDetail(
734
+ item.backtest,
735
+ backtestTimestampOffsetMs
736
+ ),
737
+ timestampDiffMs: item.timestampDiffMs,
738
+ priceDeltaPct: item.priceDeltaPct
739
+ });
740
+ return {
741
+ runtime: detail.runtime,
742
+ backtest: detail.backtest,
743
+ timestampDiffMs: detail.timestampDiffMs,
744
+ priceDeltaPct: detail.priceDeltaPct,
745
+ reason: item.reason
746
+ };
747
+ }),
748
+ limit
749
+ ),
727
750
  runtimeOnly: cappedExchangeOnlyDetails,
728
751
  backtestOnly: cappedBacktestOnlyDetails,
729
752
  nearestCandidates: cappedNearestCandidates,
@@ -22,6 +22,7 @@ var runtimeSignalsLoader_exports = {};
22
22
  __export(runtimeSignalsLoader_exports, {
23
23
  isRuntimeSignalEvaluationRecord: () => isRuntimeSignalEvaluationRecord,
24
24
  isSignalRecord: () => isSignalRecord,
25
+ loadRuntimeLineageScopes: () => loadRuntimeLineageScopes,
25
26
  loadRuntimeSignalEvaluationStatsBuckets: () => loadRuntimeSignalEvaluationStatsBuckets,
26
27
  loadRuntimeSignalEvaluations: () => loadRuntimeSignalEvaluations,
27
28
  loadRuntimeSignals: () => loadRuntimeSignals
@@ -145,6 +146,23 @@ var loadRuntimeSignalEvaluations = async (userName, {
145
146
  }
146
147
  return sortByTimestamp([...deduped.values()]);
147
148
  };
149
+ var loadRuntimeLineageScopes = async (userName, {
150
+ startTime,
151
+ endTime
152
+ }) => {
153
+ const records = (await Promise.all(
154
+ (0, import_time.getRuntimeStorageDayKeys)(startTime, endTime).map(
155
+ (dayKey) => (0, import_redis.getHashJsonValues)(
156
+ import_redis.redisKeys.runtimeLineageScopeBucket(userName, dayKey)
157
+ )
158
+ )
159
+ )).flat().filter(
160
+ (record) => record != null && typeof record.strategy === "string" && typeof record.symbol === "string" && typeof record.firstTimestamp === "number" && typeof record.lastTimestamp === "number" && record.lineage != null
161
+ );
162
+ return records.sort(
163
+ (left, right) => left.firstTimestamp - right.firstTimestamp || left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol)
164
+ );
165
+ };
148
166
  var parseBucketKey = (prefix, key) => {
149
167
  if (!key.startsWith(prefix)) {
150
168
  return null;
@@ -181,6 +199,7 @@ var loadRuntimeSignalEvaluationStatsBuckets = async (userName) => {
181
199
  0 && (module.exports = {
182
200
  isRuntimeSignalEvaluationRecord,
183
201
  isSignalRecord,
202
+ loadRuntimeLineageScopes,
184
203
  loadRuntimeSignalEvaluationStatsBuckets,
185
204
  loadRuntimeSignalEvaluations,
186
205
  loadRuntimeSignals
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var runtimeSignalsStorage_exports = {};
22
22
  __export(runtimeSignalsStorage_exports, {
23
23
  DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS: () => DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS,
24
+ RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS: () => RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS,
24
25
  RUNTIME_SIGNAL_RETENTION_DAYS_ENV: () => RUNTIME_SIGNAL_RETENTION_DAYS_ENV,
25
26
  buildRuntimeSignalStatsIncrements: () => buildRuntimeSignalStatsIncrements,
26
27
  getRuntimeSignalRetentionTtlSeconds: () => getRuntimeSignalRetentionTtlSeconds,
@@ -40,6 +41,7 @@ var import_time = require("@tradejs/core/time");
40
41
  var SECONDS_PER_DAY = 86400;
41
42
  var RUNTIME_SIGNAL_RETENTION_DAYS_ENV = "RUNTIME_SIGNAL_RETENTION_DAYS";
42
43
  var DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS = import_constants.TTL_3D;
44
+ var RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS = import_constants.TTL_1M;
43
45
  var parsePositiveNumber = (value) => {
44
46
  if (value == null || value === "") {
45
47
  return null;
@@ -170,6 +172,7 @@ var parseRuntimeSignalStatsBucket = (value) => {
170
172
  // Annotate the CommonJS export names for ESM import in node:
171
173
  0 && (module.exports = {
172
174
  DEFAULT_RUNTIME_SIGNAL_RETENTION_TTL_SECONDS,
175
+ RUNTIME_LINEAGE_SCOPE_RETENTION_TTL_SECONDS,
173
176
  RUNTIME_SIGNAL_RETENTION_DAYS_ENV,
174
177
  buildRuntimeSignalStatsIncrements,
175
178
  getRuntimeSignalRetentionTtlSeconds,
@@ -125,6 +125,18 @@ var consumeClosedPnlMatch = (buckets, trade) => {
125
125
  const [row] = rows.splice(matchIndex, 1);
126
126
  return row ?? null;
127
127
  };
128
+ var isRuntimeTradeInConnectorScope = (trade, connector) => {
129
+ if (trade.deploymentId && trade.deploymentId !== connector.deploymentId) {
130
+ return false;
131
+ }
132
+ if (trade.accountId && trade.accountId !== connector.accountId) {
133
+ return false;
134
+ }
135
+ if (trade.universe && connector.universe && trade.universe !== connector.universe) {
136
+ return false;
137
+ }
138
+ return true;
139
+ };
128
140
  var syncRuntimeTrades = async ({
129
141
  userName,
130
142
  connector,
@@ -148,16 +160,23 @@ var syncRuntimeTrades = async ({
148
160
  const openPositionsBySymbol = new Map(
149
161
  openPositions.map((position) => [position.symbol, position])
150
162
  );
151
- const activeOrderIdBySymbol = /* @__PURE__ */ new Map();
152
- const symbols = [...new Set(trades.map((trade) => trade.symbol))];
163
+ const activeOrderIdByKey = /* @__PURE__ */ new Map();
164
+ const activeTradeKeys = [
165
+ ...new Set(
166
+ trades.filter((trade) => isRuntimeTradeInConnectorScope(trade, connector)).map(
167
+ (trade) => import_redis.redisKeys.runtimeActiveTrade(
168
+ userName,
169
+ trade.symbol,
170
+ trade.deploymentId ?? trade.accountId
171
+ )
172
+ )
173
+ )
174
+ ];
153
175
  await Promise.all(
154
- symbols.map(async (symbol) => {
155
- const activeRef = await (0, import_redis.getData)(
156
- import_redis.redisKeys.runtimeActiveTrade(userName, symbol),
157
- null
158
- );
159
- activeOrderIdBySymbol.set(
160
- symbol,
176
+ activeTradeKeys.map(async (key) => {
177
+ const activeRef = await (0, import_redis.getData)(key, null);
178
+ activeOrderIdByKey.set(
179
+ key,
161
180
  typeof activeRef?.orderId === "string" ? activeRef.orderId : null
162
181
  );
163
182
  })
@@ -176,12 +195,21 @@ var syncRuntimeTrades = async ({
176
195
  }
177
196
  const syncedTrades = [];
178
197
  for (const trade of trades) {
198
+ if (!isRuntimeTradeInConnectorScope(trade, connector)) {
199
+ syncedTrades.push(trade);
200
+ continue;
201
+ }
179
202
  if (trade.status !== "active" && !isRuntimeTradeSyncFallbackClose(trade)) {
180
203
  syncedTrades.push(trade);
181
204
  continue;
182
205
  }
206
+ const activeTradeKey = import_redis.redisKeys.runtimeActiveTrade(
207
+ userName,
208
+ trade.symbol,
209
+ trade.deploymentId ?? trade.accountId
210
+ );
183
211
  const openPosition = openPositionsBySymbol.get(trade.symbol);
184
- const activeOrderId = activeOrderIdBySymbol.get(trade.symbol);
212
+ const activeOrderId = activeOrderIdByKey.get(activeTradeKey);
185
213
  const isCurrentActiveTrade = activeOrderId === trade.orderId;
186
214
  if (trade.status === "active" && !openPositionsReliable) {
187
215
  syncedTrades.push({
@@ -220,6 +248,7 @@ var syncRuntimeTrades = async ({
220
248
  }
221
249
  const matchedClosedPnl = consumeClosedPnlMatch(closedPnlBuckets, trade);
222
250
  if (!matchedClosedPnl) {
251
+ syncedTrades.push(trade);
223
252
  continue;
224
253
  }
225
254
  const nextTrade = {
@@ -252,7 +281,7 @@ var syncRuntimeTrades = async ({
252
281
  nextTrade,
253
282
  { expire: import_constants.TTL_1M }
254
283
  ),
255
- ...isCurrentActiveTrade ? [(0, import_redis.delKey)(import_redis.redisKeys.runtimeActiveTrade(userName, trade.symbol))] : []
284
+ ...isCurrentActiveTrade ? [(0, import_redis.delKey)(activeTradeKey)] : []
256
285
  ]);
257
286
  syncedTrades.push(nextTrade);
258
287
  }
@@ -50,11 +50,30 @@ var resolveTimeWindow = ({
50
50
  endTime,
51
51
  nowMs = Date.now(),
52
52
  defaultStartMs,
53
- defaultEndMs
53
+ defaultEndMs,
54
+ closedIntervalMs
54
55
  }) => {
55
56
  const explicitStart = toEpochMs(startTime);
56
57
  const explicitEnd = toEpochMs(endTime);
57
58
  const resolvedDays = toPositiveNumber(days);
59
+ const alignToLastClosedInterval = (window, preserveDuration) => {
60
+ if (typeof closedIntervalMs !== "number" || !Number.isFinite(closedIntervalMs) || closedIntervalMs <= 0) {
61
+ return window;
62
+ }
63
+ const alignedEnd = Math.floor(window.end / closedIntervalMs) * closedIntervalMs - 1;
64
+ const delta = window.end - alignedEnd;
65
+ const alignedStart = preserveDuration ? window.start - delta : window.start;
66
+ if (alignedStart >= alignedEnd) {
67
+ throw new Error(
68
+ `Invalid closed-candle window: start (${alignedStart}) must be less than end (${alignedEnd})`
69
+ );
70
+ }
71
+ return {
72
+ ...window,
73
+ start: Math.trunc(alignedStart),
74
+ end: Math.trunc(alignedEnd)
75
+ };
76
+ };
58
77
  if (explicitStart != null || explicitEnd != null) {
59
78
  const resolvedStart = explicitStart ?? (resolvedDays != null && explicitEnd != null ? explicitEnd - resolvedDays * ONE_DAY_MS : defaultStartMs);
60
79
  const resolvedEnd2 = explicitEnd ?? (resolvedDays != null && explicitStart != null ? explicitStart + resolvedDays * ONE_DAY_MS : defaultEndMs ?? nowMs);
@@ -63,11 +82,14 @@ var resolveTimeWindow = ({
63
82
  `Invalid time window: startTime (${resolvedStart}) must be less than endTime (${resolvedEnd2})`
64
83
  );
65
84
  }
66
- return {
67
- start: resolvedStart,
68
- end: resolvedEnd2,
69
- source: "explicit"
70
- };
85
+ return alignToLastClosedInterval(
86
+ {
87
+ start: resolvedStart,
88
+ end: resolvedEnd2,
89
+ source: "explicit"
90
+ },
91
+ explicitStart == null && resolvedDays != null
92
+ );
71
93
  }
72
94
  if (resolvedDays != null) {
73
95
  const resolvedEnd2 = defaultEndMs ?? nowMs;
@@ -77,11 +99,14 @@ var resolveTimeWindow = ({
77
99
  `Invalid time window: computed start (${resolvedStart}) must be less than end (${resolvedEnd2})`
78
100
  );
79
101
  }
80
- return {
81
- start: Math.trunc(resolvedStart),
82
- end: Math.trunc(resolvedEnd2),
83
- source: "days"
84
- };
102
+ return alignToLastClosedInterval(
103
+ {
104
+ start: Math.trunc(resolvedStart),
105
+ end: Math.trunc(resolvedEnd2),
106
+ source: "days"
107
+ },
108
+ true
109
+ );
85
110
  }
86
111
  const resolvedEnd = defaultEndMs ?? nowMs;
87
112
  if (defaultStartMs >= resolvedEnd) {
@@ -89,11 +114,14 @@ var resolveTimeWindow = ({
89
114
  `Invalid default time window: start (${defaultStartMs}) must be less than end (${resolvedEnd})`
90
115
  );
91
116
  }
92
- return {
93
- start: Math.trunc(defaultStartMs),
94
- end: Math.trunc(resolvedEnd),
95
- source: "default"
96
- };
117
+ return alignToLastClosedInterval(
118
+ {
119
+ start: Math.trunc(defaultStartMs),
120
+ end: Math.trunc(resolvedEnd),
121
+ source: "default"
122
+ },
123
+ true
124
+ );
97
125
  };
98
126
  // Annotate the CommonJS export names for ESM import in node:
99
127
  0 && (module.exports = {