@mastra/loggers 1.3.9-alpha.0 → 1.3.9-alpha.2

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.
@@ -3,7 +3,7 @@ name: mastra-loggers
3
3
  description: Documentation for @mastra/loggers. Use when working with @mastra/loggers APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/loggers"
6
- version: "1.3.9-alpha.0"
6
+ version: "1.3.9-alpha.2"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.3.9-alpha.0",
2
+ "version": "1.3.9-alpha.2",
3
3
  "package": "@mastra/loggers",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -147,6 +147,12 @@ Re-drives every orphaned `running` durable-agent run across all registered durab
147
147
 
148
148
  Requires persistent storage. With an in-memory store, there's nothing to recover after a process restart.
149
149
 
150
+ Agents and their runs are attempted serially. Each built-in `recoverActiveRuns()` attempt waits up to 30 seconds in total for `recover()` setup and settlement of the whole recovered workflow, then traversal can continue to later runs and agents. This isn't an overall recovery deadline: discovery and overridden custom recovery methods aren't bounded by this budget.
151
+
152
+ A timed-out attempt continues in the background. Its outcome is `status: 'skipped'` with `reason: 'recovery-timeout'` and no `retryAt`. It contributes only to `skipped`, not `recovered`, `succeeded`, or `failed`, and Mastra doesn't automatically retry it. Late settlement doesn't change the returned counts.
153
+
154
+ When shutdown begins, recovery stops dispatching new agents and runs. The recovery traversal and original attempts, including timed-out setup or execution, participate in the same shared shutdown drain as active executions. A recovery wait timeout doesn't cancel the attempt or give it a separate shutdown drain window.
155
+
150
156
  A run that another execution still drives isn't taken over. It's counted in `skipped` and checked again once that execution's claim could have lapsed, so a run whose process crashed is recovered without another call. `shutdown()` cancels pending re-checks. See [run ownership](https://mastra.ai/docs/harness/durable-agents).
151
157
 
152
158
  ```typescript
@@ -164,4 +170,4 @@ Returns:
164
170
 
165
171
  **failed** (`number`): Runs whose restart threw an error.
166
172
 
167
- **skipped** (`number`): Runs that another execution still drives. Each is checked again later.
173
+ **skipped** (`number`): Runs skipped because they are already active, recovery is already in progress, or the recovery wait timed out. Only ownership skips eligible for re-checks are checked again; recovery timeouts are not automatically retried.
@@ -59,6 +59,11 @@ var HttpTransport = class extends _mastra_core_logger.LoggerTransport {
59
59
  this.logBuffer = [];
60
60
  this.lastFlush = Date.now();
61
61
  this.flushIntervalId = setInterval(() => this.requestFlush(), this.flushInterval);
62
+ this.updateIntervalRef();
63
+ }
64
+ updateIntervalRef() {
65
+ if (this.logBuffer.length > 0 || this.flushPromise) this.flushIntervalId.ref?.();
66
+ else this.flushIntervalId.unref?.();
62
67
  }
63
68
  async makeHttpRequest(data) {
64
69
  const body = JSON.stringify({ logs: data });
@@ -114,6 +119,7 @@ var HttpTransport = class extends _mastra_core_logger.LoggerTransport {
114
119
  this.flushRequested = false;
115
120
  const flush = this.flushBatch().finally(() => {
116
121
  this.flushPromise = null;
122
+ this.updateIntervalRef();
117
123
  });
118
124
  this.flushPromise = flush;
119
125
  const continueDraining = () => {
@@ -160,6 +166,7 @@ var HttpTransport = class extends _mastra_core_logger.LoggerTransport {
160
166
  } : parsed;
161
167
  this.logBuffer.push(log);
162
168
  this.enforceBufferLimit();
169
+ this.updateIntervalRef();
163
170
  if (this.logBuffer.length >= this.batchSize) this.requestFlush();
164
171
  cb(null, chunk);
165
172
  } catch (error) {
@@ -204,6 +211,7 @@ var HttpTransport = class extends _mastra_core_logger.LoggerTransport {
204
211
  }
205
212
  clearBuffer() {
206
213
  this.logBuffer = [];
214
+ this.updateIntervalRef();
207
215
  }
208
216
  getDroppedLogCount() {
209
217
  return this.droppedLogCount;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["LoggerTransport"],"sources":["../../src/http/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\n\ninterface RetryOptions {\n maxRetries?: number;\n retryDelay?: number;\n exponentialBackoff?: boolean;\n}\n\ninterface HttpTransportOptions {\n url: string;\n method?: 'POST' | 'PUT' | 'PATCH';\n headers?: Record<string, string>;\n batchSize?: number;\n flushInterval?: number;\n timeout?: number;\n retryOptions?: RetryOptions;\n maxBufferSize?: number;\n}\n\nclass HttpResponseError extends Error {\n constructor(\n readonly status: number,\n statusText: string,\n readonly retryAfterMs: number | undefined,\n ) {\n super(`HTTP ${status}: ${statusText}`);\n }\n}\n\n// Other 4xx responses mean the request itself is wrong and will fail the same way on every retry.\nfunction isRetryableStatus(status: number): boolean {\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\n// Retry-After is either delta-seconds or an HTTP date (RFC 9110). Invalid values are ignored.\nfunction parseRetryAfter(headers: Headers | undefined): number | undefined {\n const value = headers?.get('retry-after')?.trim();\n if (!value) return undefined;\n if (/^\\d+$/.test(value)) return Number(value) * 1000;\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nexport class HttpTransport extends LoggerTransport {\n private url: string;\n private method: string;\n private headers: Record<string, string>;\n private batchSize: number;\n private flushInterval: number;\n private timeout: number;\n private retryOptions: Required<RetryOptions>;\n private logBuffer: BaseLogMessage[];\n private maxBufferSize: number;\n private droppedLogCount = 0;\n private flushPromise: Promise<void> | null = null;\n private flushRequested = false;\n private lastFlush: number;\n private flushIntervalId: NodeJS.Timeout;\n\n constructor(options: HttpTransportOptions) {\n super({ objectMode: true });\n\n if (!options.url) {\n throw new Error('HTTP URL is required');\n }\n\n this.url = options.url;\n this.method = options.method || 'POST';\n this.headers = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n this.batchSize = options.batchSize ?? 100;\n this.flushInterval = options.flushInterval || 10000;\n this.timeout = options.timeout || 30000;\n this.retryOptions = {\n maxRetries: options.retryOptions?.maxRetries ?? 3,\n retryDelay: options.retryOptions?.retryDelay ?? 1000,\n exponentialBackoff: options.retryOptions?.exponentialBackoff ?? true,\n };\n\n if (!Number.isInteger(this.batchSize) || this.batchSize < 1) {\n throw new Error('HttpTransport batchSize must be a positive integer');\n }\n\n const maxBufferSize = options.maxBufferSize ?? 10_000;\n if (!Number.isInteger(maxBufferSize) || maxBufferSize < 1) {\n throw new Error('HttpTransport maxBufferSize must be a positive integer');\n }\n this.maxBufferSize = Math.max(maxBufferSize, this.batchSize);\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => this.requestFlush(), this.flushInterval);\n }\n\n private async makeHttpRequest(data: any): Promise<Response> {\n // Serialize once, outside the retry loop: serialization failures are not retryable,\n // and every retry must send the exact same payload.\n const body = JSON.stringify({ logs: data });\n return this.sendWithRetry(body);\n }\n\n private async sendWithRetry(body: string, retryCount = 0): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(this.url, {\n method: this.method,\n headers: this.headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new HttpResponseError(response.status, response.statusText, parseRetryAfter(response.headers));\n }\n\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n\n const isPermanent = error instanceof HttpResponseError && !isRetryableStatus(error.status);\n if (!isPermanent && retryCount < this.retryOptions.maxRetries) {\n const backoff = this.retryOptions.exponentialBackoff\n ? this.retryOptions.retryDelay * Math.pow(2, retryCount)\n : this.retryOptions.retryDelay;\n // Honor the server's Retry-After, capped at the request timeout so a large value can't stall shutdown.\n const retryAfter = error instanceof HttpResponseError ? (error.retryAfterMs ?? 0) : 0;\n const delay = Math.max(backoff, Math.min(retryAfter, this.timeout));\n\n await new Promise(resolve => setTimeout(resolve, delay));\n return this.sendWithRetry(body, retryCount + 1);\n }\n\n throw error;\n }\n }\n\n private enforceBufferLimit(): void {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow <= 0) {\n return;\n }\n\n this.logBuffer.splice(0, overflow);\n if (this.droppedLogCount === 0) {\n console.warn(\n `HttpTransport: buffer exceeded maxBufferSize (${this.maxBufferSize}); dropping oldest logs. Use getDroppedLogCount() to track drops.`,\n );\n }\n this.droppedLogCount += overflow;\n }\n\n // Internal fire-and-forget flush. While a request is in flight this only records the request, so writes\n // during a slow or retrying request don't each attach another handler to the pending promise.\n private requestFlush(): void {\n if (this.flushPromise) {\n this.flushRequested = true;\n return;\n }\n this._flush().catch(err => {\n console.error('Error flushing logs to HTTP endpoint:', err);\n });\n }\n\n _flush(): Promise<void> {\n // Only one request in flight at a time, so an outage doesn't fan out into overlapping retry chains.\n if (this.flushPromise) {\n this.flushRequested = true;\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushRequested = false;\n const flush = this.flushBatch().finally(() => {\n this.flushPromise = null;\n });\n this.flushPromise = flush;\n // Flushes requested while this request was in flight only got this promise back, so send their logs now,\n // and keep draining full batches. On a transient failure, wait for the next interval instead of retrying\n // straight away. A permanently rejected batch was dropped, so the logs behind it can go out now.\n const continueDraining = () => {\n if (this.logBuffer.length >= this.batchSize || (this.flushRequested && this.logBuffer.length > 0)) {\n this.requestFlush();\n }\n };\n flush.then(continueDraining, error => {\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n continueDraining();\n }\n });\n return flush;\n }\n\n private async flushBatch(): Promise<void> {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n\n try {\n await this.makeHttpRequest(logs);\n this.lastFlush = now;\n } catch (error) {\n // The endpoint rejected this batch outright, so resending it can't succeed and would block the logs behind it.\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n this.droppedLogCount += logs.length;\n console.warn(\n `HttpTransport: endpoint rejected a batch with HTTP ${error.status}; dropping ${logs.length} logs. Use getDroppedLogCount() to track drops.`,\n );\n throw error;\n }\n\n // On a transient error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function): void {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n this.enforceBufferLimit();\n\n // Flush if buffer reaches batch size\n if (this.logBuffer.length >= this.batchSize) {\n this.requestFlush();\n }\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function): void {\n clearInterval(this.flushIntervalId);\n\n // Final drain. Wait out any in-flight request (ignoring its failure, since its logs are back in the buffer),\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n if (this.logBuffer.length > 0 || this.flushPromise) {\n const drain = async () => {\n await this.flushPromise?.catch(() => {});\n while (this.flushPromise || this.logBuffer.length > 0) {\n await (this.flushPromise ?? this._flush());\n }\n };\n\n drain()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogs: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: params?.page === 0 ? 1 : (params?.page ?? 1),\n perPage: params?.perPage ?? 100,\n hasMore: false,\n };\n }\n\n async listLogsByRunId({\n runId: _runId,\n fromDate: _fromDate,\n toDate: _toDate,\n logLevel: _logLevel,\n filters: _filters,\n page,\n perPage,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogsByRunId: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: page === 0 ? 1 : (page ?? 1),\n perPage: perPage ?? 100,\n hasMore: false,\n };\n }\n\n // Utility methods\n public getBufferedLogs(): BaseLogMessage[] {\n return [...this.logBuffer];\n }\n\n public clearBuffer(): void {\n this.logBuffer = [];\n }\n\n public getDroppedLogCount(): number {\n return this.droppedLogCount;\n }\n\n public getLastFlushTime(): number {\n return this.lastFlush;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["LoggerTransport"],"sources":["../../src/http/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\n\ninterface RetryOptions {\n maxRetries?: number;\n retryDelay?: number;\n exponentialBackoff?: boolean;\n}\n\ninterface HttpTransportOptions {\n url: string;\n method?: 'POST' | 'PUT' | 'PATCH';\n headers?: Record<string, string>;\n batchSize?: number;\n flushInterval?: number;\n timeout?: number;\n retryOptions?: RetryOptions;\n maxBufferSize?: number;\n}\n\nclass HttpResponseError extends Error {\n constructor(\n readonly status: number,\n statusText: string,\n readonly retryAfterMs: number | undefined,\n ) {\n super(`HTTP ${status}: ${statusText}`);\n }\n}\n\n// Other 4xx responses mean the request itself is wrong and will fail the same way on every retry.\nfunction isRetryableStatus(status: number): boolean {\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\n// Retry-After is either delta-seconds or an HTTP date (RFC 9110). Invalid values are ignored.\nfunction parseRetryAfter(headers: Headers | undefined): number | undefined {\n const value = headers?.get('retry-after')?.trim();\n if (!value) return undefined;\n if (/^\\d+$/.test(value)) return Number(value) * 1000;\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nexport class HttpTransport extends LoggerTransport {\n private url: string;\n private method: string;\n private headers: Record<string, string>;\n private batchSize: number;\n private flushInterval: number;\n private timeout: number;\n private retryOptions: Required<RetryOptions>;\n private logBuffer: BaseLogMessage[];\n private maxBufferSize: number;\n private droppedLogCount = 0;\n private flushPromise: Promise<void> | null = null;\n private flushRequested = false;\n private lastFlush: number;\n private flushIntervalId: NodeJS.Timeout;\n\n constructor(options: HttpTransportOptions) {\n super({ objectMode: true });\n\n if (!options.url) {\n throw new Error('HTTP URL is required');\n }\n\n this.url = options.url;\n this.method = options.method || 'POST';\n this.headers = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n this.batchSize = options.batchSize ?? 100;\n this.flushInterval = options.flushInterval || 10000;\n this.timeout = options.timeout || 30000;\n this.retryOptions = {\n maxRetries: options.retryOptions?.maxRetries ?? 3,\n retryDelay: options.retryOptions?.retryDelay ?? 1000,\n exponentialBackoff: options.retryOptions?.exponentialBackoff ?? true,\n };\n\n if (!Number.isInteger(this.batchSize) || this.batchSize < 1) {\n throw new Error('HttpTransport batchSize must be a positive integer');\n }\n\n const maxBufferSize = options.maxBufferSize ?? 10_000;\n if (!Number.isInteger(maxBufferSize) || maxBufferSize < 1) {\n throw new Error('HttpTransport maxBufferSize must be a positive integer');\n }\n this.maxBufferSize = Math.max(maxBufferSize, this.batchSize);\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => this.requestFlush(), this.flushInterval);\n this.updateIntervalRef();\n }\n\n // The interval only keeps the process alive while logs are waiting to be sent, so an idle transport\n // doesn't stop a script from exiting, but buffered logs still get delivered (or retried) first.\n // Web-standard runtimes return a numeric timer id without ref()/unref(), so those calls are optional.\n private updateIntervalRef(): void {\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.flushIntervalId.ref?.();\n } else {\n this.flushIntervalId.unref?.();\n }\n }\n\n private async makeHttpRequest(data: any): Promise<Response> {\n // Serialize once, outside the retry loop: serialization failures are not retryable,\n // and every retry must send the exact same payload.\n const body = JSON.stringify({ logs: data });\n return this.sendWithRetry(body);\n }\n\n private async sendWithRetry(body: string, retryCount = 0): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(this.url, {\n method: this.method,\n headers: this.headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new HttpResponseError(response.status, response.statusText, parseRetryAfter(response.headers));\n }\n\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n\n const isPermanent = error instanceof HttpResponseError && !isRetryableStatus(error.status);\n if (!isPermanent && retryCount < this.retryOptions.maxRetries) {\n const backoff = this.retryOptions.exponentialBackoff\n ? this.retryOptions.retryDelay * Math.pow(2, retryCount)\n : this.retryOptions.retryDelay;\n // Honor the server's Retry-After, capped at the request timeout so a large value can't stall shutdown.\n const retryAfter = error instanceof HttpResponseError ? (error.retryAfterMs ?? 0) : 0;\n const delay = Math.max(backoff, Math.min(retryAfter, this.timeout));\n\n await new Promise(resolve => setTimeout(resolve, delay));\n return this.sendWithRetry(body, retryCount + 1);\n }\n\n throw error;\n }\n }\n\n private enforceBufferLimit(): void {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow <= 0) {\n return;\n }\n\n this.logBuffer.splice(0, overflow);\n if (this.droppedLogCount === 0) {\n console.warn(\n `HttpTransport: buffer exceeded maxBufferSize (${this.maxBufferSize}); dropping oldest logs. Use getDroppedLogCount() to track drops.`,\n );\n }\n this.droppedLogCount += overflow;\n }\n\n // Internal fire-and-forget flush. While a request is in flight this only records the request, so writes\n // during a slow or retrying request don't each attach another handler to the pending promise.\n private requestFlush(): void {\n if (this.flushPromise) {\n this.flushRequested = true;\n return;\n }\n this._flush().catch(err => {\n console.error('Error flushing logs to HTTP endpoint:', err);\n });\n }\n\n _flush(): Promise<void> {\n // Only one request in flight at a time, so an outage doesn't fan out into overlapping retry chains.\n if (this.flushPromise) {\n this.flushRequested = true;\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushRequested = false;\n const flush = this.flushBatch().finally(() => {\n this.flushPromise = null;\n this.updateIntervalRef();\n });\n this.flushPromise = flush;\n // Flushes requested while this request was in flight only got this promise back, so send their logs now,\n // and keep draining full batches. On a transient failure, wait for the next interval instead of retrying\n // straight away. A permanently rejected batch was dropped, so the logs behind it can go out now.\n const continueDraining = () => {\n if (this.logBuffer.length >= this.batchSize || (this.flushRequested && this.logBuffer.length > 0)) {\n this.requestFlush();\n }\n };\n flush.then(continueDraining, error => {\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n continueDraining();\n }\n });\n return flush;\n }\n\n private async flushBatch(): Promise<void> {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n\n try {\n await this.makeHttpRequest(logs);\n this.lastFlush = now;\n } catch (error) {\n // The endpoint rejected this batch outright, so resending it can't succeed and would block the logs behind it.\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n this.droppedLogCount += logs.length;\n console.warn(\n `HttpTransport: endpoint rejected a batch with HTTP ${error.status}; dropping ${logs.length} logs. Use getDroppedLogCount() to track drops.`,\n );\n throw error;\n }\n\n // On a transient error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function): void {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n this.enforceBufferLimit();\n this.updateIntervalRef();\n\n // Flush if buffer reaches batch size\n if (this.logBuffer.length >= this.batchSize) {\n this.requestFlush();\n }\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function): void {\n clearInterval(this.flushIntervalId);\n\n // Final drain. Wait out any in-flight request (ignoring its failure, since its logs are back in the buffer),\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n if (this.logBuffer.length > 0 || this.flushPromise) {\n const drain = async () => {\n await this.flushPromise?.catch(() => {});\n while (this.flushPromise || this.logBuffer.length > 0) {\n await (this.flushPromise ?? this._flush());\n }\n };\n\n drain()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogs: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: params?.page === 0 ? 1 : (params?.page ?? 1),\n perPage: params?.perPage ?? 100,\n hasMore: false,\n };\n }\n\n async listLogsByRunId({\n runId: _runId,\n fromDate: _fromDate,\n toDate: _toDate,\n logLevel: _logLevel,\n filters: _filters,\n page,\n perPage,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogsByRunId: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: page === 0 ? 1 : (page ?? 1),\n perPage: perPage ?? 100,\n hasMore: false,\n };\n }\n\n // Utility methods\n public getBufferedLogs(): BaseLogMessage[] {\n return [...this.logBuffer];\n }\n\n public clearBuffer(): void {\n this.logBuffer = [];\n this.updateIntervalRef();\n }\n\n public getDroppedLogCount(): number {\n return this.droppedLogCount;\n }\n\n public getLastFlushTime(): number {\n return this.lastFlush;\n }\n}\n"],"mappings":";;;AAoBA,IAAM,oBAAN,cAAgC,MAAM;CAEzB;CAEA;CAHX,YACE,QACA,YACA,cACA;EACA,MAAM,QAAQ,OAAO,IAAI,YAAY;EAJ5B,KAAA,SAAA;EAEA,KAAA,eAAA;CAGX;AACF;AAGA,SAAS,kBAAkB,QAAyB;CAClD,OAAO,WAAW,OAAO,WAAW,OAAO,WAAW,OAAO,UAAU;AACzE;AAGA,SAAS,gBAAgB,SAAkD;CACzE,MAAM,QAAQ,SAAS,IAAI,aAAa,CAAC,EAAE,KAAK;CAChD,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,IAAI,QAAQ,KAAK,KAAK,GAAG,OAAO,OAAO,KAAK,IAAI;CAChD,MAAM,OAAO,KAAK,MAAM,KAAK;CAC7B,OAAO,OAAO,MAAM,IAAI,IAAI,KAAA,IAAY,KAAK,IAAI,GAAG,OAAO,KAAK,IAAI,CAAC;AACvE;AAEA,IAAa,gBAAb,cAAmCA,oBAAAA,gBAAgB;CACjD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,kBAA0B;CAC1B,eAA6C;CAC7C,iBAAyB;CACzB;CACA;CAEA,YAAY,SAA+B;EACzC,MAAM,EAAE,YAAY,KAAK,CAAC;EAE1B,IAAI,CAAC,QAAQ,KACX,MAAM,IAAI,MAAM,sBAAsB;EAGxC,KAAK,MAAM,QAAQ;EACnB,KAAK,SAAS,QAAQ,UAAU;EAChC,KAAK,UAAU;GACb,gBAAgB;GAChB,GAAG,QAAQ;EACb;EACA,KAAK,YAAY,QAAQ,aAAa;EACtC,KAAK,gBAAgB,QAAQ,iBAAiB;EAC9C,KAAK,UAAU,QAAQ,WAAW;EAClC,KAAK,eAAe;GAClB,YAAY,QAAQ,cAAc,cAAc;GAChD,YAAY,QAAQ,cAAc,cAAc;GAChD,oBAAoB,QAAQ,cAAc,sBAAsB;EAClE;EAEA,IAAI,CAAC,OAAO,UAAU,KAAK,SAAS,KAAK,KAAK,YAAY,GACxD,MAAM,IAAI,MAAM,oDAAoD;EAGtE,MAAM,gBAAgB,QAAQ,iBAAiB;EAC/C,IAAI,CAAC,OAAO,UAAU,aAAa,KAAK,gBAAgB,GACtD,MAAM,IAAI,MAAM,wDAAwD;EAE1E,KAAK,gBAAgB,KAAK,IAAI,eAAe,KAAK,SAAS;EAC3D,KAAK,YAAY,CAAC;EAClB,KAAK,YAAY,KAAK,IAAI;EAG1B,KAAK,kBAAkB,kBAAkB,KAAK,aAAa,GAAG,KAAK,aAAa;EAChF,KAAK,kBAAkB;CACzB;CAKA,oBAAkC;EAChC,IAAI,KAAK,UAAU,SAAS,KAAK,KAAK,cACpC,KAAK,gBAAgB,MAAM;OAE3B,KAAK,gBAAgB,QAAQ;CAEjC;CAEA,MAAc,gBAAgB,MAA8B;EAG1D,MAAM,OAAO,KAAK,UAAU,EAAE,MAAM,KAAK,CAAC;EAC1C,OAAO,KAAK,cAAc,IAAI;CAChC;CAEA,MAAc,cAAc,MAAc,aAAa,GAAsB;EAC3E,MAAM,aAAa,IAAI,gBAAgB;EACvC,MAAM,YAAY,iBAAiB,WAAW,MAAM,GAAG,KAAK,OAAO;EAEnE,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,KAAK,KAAK;IACrC,QAAQ,KAAK;IACb,SAAS,KAAK;IACd;IACA,QAAQ,WAAW;GACrB,CAAC;GAED,aAAa,SAAS;GAEtB,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,kBAAkB,SAAS,QAAQ,SAAS,YAAY,gBAAgB,SAAS,OAAO,CAAC;GAGrG,OAAO;EACT,SAAS,OAAO;GACd,aAAa,SAAS;GAGtB,IAAI,EADgB,iBAAiB,qBAAqB,CAAC,kBAAkB,MAAM,MAAM,MACrE,aAAa,KAAK,aAAa,YAAY;IAC7D,MAAM,UAAU,KAAK,aAAa,qBAC9B,KAAK,aAAa,aAAa,KAAK,IAAI,GAAG,UAAU,IACrD,KAAK,aAAa;IAEtB,MAAM,aAAa,iBAAiB,oBAAqB,MAAM,gBAAgB,IAAK;IACpF,MAAM,QAAQ,KAAK,IAAI,SAAS,KAAK,IAAI,YAAY,KAAK,OAAO,CAAC;IAElE,MAAM,IAAI,SAAQ,YAAW,WAAW,SAAS,KAAK,CAAC;IACvD,OAAO,KAAK,cAAc,MAAM,aAAa,CAAC;GAChD;GAEA,MAAM;EACR;CACF;CAEA,qBAAmC;EACjC,MAAM,WAAW,KAAK,UAAU,SAAS,KAAK;EAC9C,IAAI,YAAY,GACd;EAGF,KAAK,UAAU,OAAO,GAAG,QAAQ;EACjC,IAAI,KAAK,oBAAoB,GAC3B,QAAQ,KACN,iDAAiD,KAAK,cAAc,kEACtE;EAEF,KAAK,mBAAmB;CAC1B;CAIA,eAA6B;EAC3B,IAAI,KAAK,cAAc;GACrB,KAAK,iBAAiB;GACtB;EACF;EACA,KAAK,OAAO,CAAC,CAAC,OAAM,QAAO;GACzB,QAAQ,MAAM,yCAAyC,GAAG;EAC5D,CAAC;CACH;CAEA,SAAwB;EAEtB,IAAI,KAAK,cAAc;GACrB,KAAK,iBAAiB;GACtB,OAAO,KAAK;EACd;EACA,IAAI,KAAK,UAAU,WAAW,GAC5B,OAAO,QAAQ,QAAQ;EAGzB,KAAK,iBAAiB;EACtB,MAAM,QAAQ,KAAK,WAAW,CAAC,CAAC,cAAc;GAC5C,KAAK,eAAe;GACpB,KAAK,kBAAkB;EACzB,CAAC;EACD,KAAK,eAAe;EAIpB,MAAM,yBAAyB;GAC7B,IAAI,KAAK,UAAU,UAAU,KAAK,aAAc,KAAK,kBAAkB,KAAK,UAAU,SAAS,GAC7F,KAAK,aAAa;EAEtB;EACA,MAAM,KAAK,mBAAkB,UAAS;GACpC,IAAI,iBAAiB,qBAAqB,CAAC,kBAAkB,MAAM,MAAM,GACvE,iBAAiB;EAErB,CAAC;EACD,OAAO;CACT;CAEA,MAAc,aAA4B;EACxC,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,OAAO,KAAK,UAAU,OAAO,GAAG,KAAK,SAAS;EAEpD,IAAI;GACF,MAAM,KAAK,gBAAgB,IAAI;GAC/B,KAAK,YAAY;EACnB,SAAS,OAAO;GAEd,IAAI,iBAAiB,qBAAqB,CAAC,kBAAkB,MAAM,MAAM,GAAG;IAC1E,KAAK,mBAAmB,KAAK;IAC7B,QAAQ,KACN,sDAAsD,MAAM,OAAO,aAAa,KAAK,OAAO,gDAC9F;IACA,MAAM;GACR;GAGA,KAAK,UAAU,QAAQ,GAAG,IAAI;GAC9B,KAAK,mBAAmB;GACxB,MAAM;EACR;CACF;CAEA,OAAO,OAAY,UAAmB,UAAoD;EACxF,IAAI,OAAO,aAAa,YAAY;GAClC,KAAK,WAAW,OAAO,YAAY,QAAQ,QAAQ;GACnD,OAAO;EACT;EAEA,KAAK,WAAW,OAAO,YAAY,SAAS,UAAwB;GAClE,IAAI,OAAO,QAAQ,MAAM,6BAA6B,KAAK;EAC7D,CAAC;EACD,OAAO;CACT;CAEA,WAAW,OAAe,MAAc,IAAoB;EAC1D,IAAI;GAEF,MAAM,SAAS,OAAO,UAAU,WAAW,KAAK,MAAM,KAAK,IAAI;GAG/D,MAAM,MAAM,OAAO,SAAS,KAAA,KAAa,OAAO,SAAS,OAAO;IAAE,GAAG;IAAQ,MAAM,KAAK,IAAI;GAAE,IAAI;GAGlG,KAAK,UAAU,KAAK,GAAG;GACvB,KAAK,mBAAmB;GACxB,KAAK,kBAAkB;GAGvB,IAAI,KAAK,UAAU,UAAU,KAAK,WAChC,KAAK,aAAa;GAIpB,GAAG,MAAM,KAAK;EAChB,SAAS,OAAO;GACd,GAAG,KAAK;EACV;CACF;CAEA,SAAS,KAAY,IAAoB;EACvC,cAAc,KAAK,eAAe;EAIlC,IAAI,KAAK,UAAU,SAAS,KAAK,KAAK,cAAc;GAClD,MAAM,QAAQ,YAAY;IACxB,MAAM,KAAK,cAAc,YAAY,CAAC,CAAC;IACvC,OAAO,KAAK,gBAAgB,KAAK,UAAU,SAAS,GAClD,OAAO,KAAK,gBAAgB,KAAK,OAAO;GAE5C;GAEA,MAAM,CAAC,CACJ,WAAW,GAAG,GAAG,CAAC,CAAC,CACnB,OAAM,aAAY;IACjB,QAAQ,MAAM,yBAAyB,QAAQ;IAC/C,GAAG,OAAO,QAAQ;GACpB,CAAC;EACL,OACE,GAAG,GAAG;CAEV;CAEA,MAAM,SAAS,QAcZ;EAGD,QAAQ,KACN,wGACF;EAEA,OAAO;GACL,MAAM,CAAC;GACP,OAAO;GACP,MAAM,QAAQ,SAAS,IAAI,IAAK,QAAQ,QAAQ;GAChD,SAAS,QAAQ,WAAW;GAC5B,SAAS;EACX;CACF;CAEA,MAAM,gBAAgB,EACpB,OAAO,QACP,UAAU,WACV,QAAQ,SACR,UAAU,WACV,SAAS,UACT,MACA,WAeC;EAGD,QAAQ,KACN,+GACF;EAEA,OAAO;GACL,MAAM,CAAC;GACP,OAAO;GACP,MAAM,SAAS,IAAI,IAAK,QAAQ;GAChC,SAAS,WAAW;GACpB,SAAS;EACX;CACF;CAGA,kBAA2C;EACzC,OAAO,CAAC,GAAG,KAAK,SAAS;CAC3B;CAEA,cAA2B;EACzB,KAAK,YAAY,CAAC;EAClB,KAAK,kBAAkB;CACzB;CAEA,qBAAoC;EAClC,OAAO,KAAK;CACd;CAEA,mBAAkC;EAChC,OAAO,KAAK;CACd;AACF"}
@@ -31,6 +31,7 @@ export declare class HttpTransport extends LoggerTransport {
31
31
  private lastFlush;
32
32
  private flushIntervalId;
33
33
  constructor(options: HttpTransportOptions);
34
+ private updateIntervalRef;
34
35
  private makeHttpRequest;
35
36
  private sendWithRetry;
36
37
  private enforceBufferLimit;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"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"}
@@ -58,6 +58,11 @@ var HttpTransport = class extends LoggerTransport {
58
58
  this.logBuffer = [];
59
59
  this.lastFlush = Date.now();
60
60
  this.flushIntervalId = setInterval(() => this.requestFlush(), this.flushInterval);
61
+ this.updateIntervalRef();
62
+ }
63
+ updateIntervalRef() {
64
+ if (this.logBuffer.length > 0 || this.flushPromise) this.flushIntervalId.ref?.();
65
+ else this.flushIntervalId.unref?.();
61
66
  }
62
67
  async makeHttpRequest(data) {
63
68
  const body = JSON.stringify({ logs: data });
@@ -113,6 +118,7 @@ var HttpTransport = class extends LoggerTransport {
113
118
  this.flushRequested = false;
114
119
  const flush = this.flushBatch().finally(() => {
115
120
  this.flushPromise = null;
121
+ this.updateIntervalRef();
116
122
  });
117
123
  this.flushPromise = flush;
118
124
  const continueDraining = () => {
@@ -159,6 +165,7 @@ var HttpTransport = class extends LoggerTransport {
159
165
  } : parsed;
160
166
  this.logBuffer.push(log);
161
167
  this.enforceBufferLimit();
168
+ this.updateIntervalRef();
162
169
  if (this.logBuffer.length >= this.batchSize) this.requestFlush();
163
170
  cb(null, chunk);
164
171
  } catch (error) {
@@ -203,6 +210,7 @@ var HttpTransport = class extends LoggerTransport {
203
210
  }
204
211
  clearBuffer() {
205
212
  this.logBuffer = [];
213
+ this.updateIntervalRef();
206
214
  }
207
215
  getDroppedLogCount() {
208
216
  return this.droppedLogCount;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/http/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\n\ninterface RetryOptions {\n maxRetries?: number;\n retryDelay?: number;\n exponentialBackoff?: boolean;\n}\n\ninterface HttpTransportOptions {\n url: string;\n method?: 'POST' | 'PUT' | 'PATCH';\n headers?: Record<string, string>;\n batchSize?: number;\n flushInterval?: number;\n timeout?: number;\n retryOptions?: RetryOptions;\n maxBufferSize?: number;\n}\n\nclass HttpResponseError extends Error {\n constructor(\n readonly status: number,\n statusText: string,\n readonly retryAfterMs: number | undefined,\n ) {\n super(`HTTP ${status}: ${statusText}`);\n }\n}\n\n// Other 4xx responses mean the request itself is wrong and will fail the same way on every retry.\nfunction isRetryableStatus(status: number): boolean {\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\n// Retry-After is either delta-seconds or an HTTP date (RFC 9110). Invalid values are ignored.\nfunction parseRetryAfter(headers: Headers | undefined): number | undefined {\n const value = headers?.get('retry-after')?.trim();\n if (!value) return undefined;\n if (/^\\d+$/.test(value)) return Number(value) * 1000;\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nexport class HttpTransport extends LoggerTransport {\n private url: string;\n private method: string;\n private headers: Record<string, string>;\n private batchSize: number;\n private flushInterval: number;\n private timeout: number;\n private retryOptions: Required<RetryOptions>;\n private logBuffer: BaseLogMessage[];\n private maxBufferSize: number;\n private droppedLogCount = 0;\n private flushPromise: Promise<void> | null = null;\n private flushRequested = false;\n private lastFlush: number;\n private flushIntervalId: NodeJS.Timeout;\n\n constructor(options: HttpTransportOptions) {\n super({ objectMode: true });\n\n if (!options.url) {\n throw new Error('HTTP URL is required');\n }\n\n this.url = options.url;\n this.method = options.method || 'POST';\n this.headers = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n this.batchSize = options.batchSize ?? 100;\n this.flushInterval = options.flushInterval || 10000;\n this.timeout = options.timeout || 30000;\n this.retryOptions = {\n maxRetries: options.retryOptions?.maxRetries ?? 3,\n retryDelay: options.retryOptions?.retryDelay ?? 1000,\n exponentialBackoff: options.retryOptions?.exponentialBackoff ?? true,\n };\n\n if (!Number.isInteger(this.batchSize) || this.batchSize < 1) {\n throw new Error('HttpTransport batchSize must be a positive integer');\n }\n\n const maxBufferSize = options.maxBufferSize ?? 10_000;\n if (!Number.isInteger(maxBufferSize) || maxBufferSize < 1) {\n throw new Error('HttpTransport maxBufferSize must be a positive integer');\n }\n this.maxBufferSize = Math.max(maxBufferSize, this.batchSize);\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => this.requestFlush(), this.flushInterval);\n }\n\n private async makeHttpRequest(data: any): Promise<Response> {\n // Serialize once, outside the retry loop: serialization failures are not retryable,\n // and every retry must send the exact same payload.\n const body = JSON.stringify({ logs: data });\n return this.sendWithRetry(body);\n }\n\n private async sendWithRetry(body: string, retryCount = 0): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(this.url, {\n method: this.method,\n headers: this.headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new HttpResponseError(response.status, response.statusText, parseRetryAfter(response.headers));\n }\n\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n\n const isPermanent = error instanceof HttpResponseError && !isRetryableStatus(error.status);\n if (!isPermanent && retryCount < this.retryOptions.maxRetries) {\n const backoff = this.retryOptions.exponentialBackoff\n ? this.retryOptions.retryDelay * Math.pow(2, retryCount)\n : this.retryOptions.retryDelay;\n // Honor the server's Retry-After, capped at the request timeout so a large value can't stall shutdown.\n const retryAfter = error instanceof HttpResponseError ? (error.retryAfterMs ?? 0) : 0;\n const delay = Math.max(backoff, Math.min(retryAfter, this.timeout));\n\n await new Promise(resolve => setTimeout(resolve, delay));\n return this.sendWithRetry(body, retryCount + 1);\n }\n\n throw error;\n }\n }\n\n private enforceBufferLimit(): void {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow <= 0) {\n return;\n }\n\n this.logBuffer.splice(0, overflow);\n if (this.droppedLogCount === 0) {\n console.warn(\n `HttpTransport: buffer exceeded maxBufferSize (${this.maxBufferSize}); dropping oldest logs. Use getDroppedLogCount() to track drops.`,\n );\n }\n this.droppedLogCount += overflow;\n }\n\n // Internal fire-and-forget flush. While a request is in flight this only records the request, so writes\n // during a slow or retrying request don't each attach another handler to the pending promise.\n private requestFlush(): void {\n if (this.flushPromise) {\n this.flushRequested = true;\n return;\n }\n this._flush().catch(err => {\n console.error('Error flushing logs to HTTP endpoint:', err);\n });\n }\n\n _flush(): Promise<void> {\n // Only one request in flight at a time, so an outage doesn't fan out into overlapping retry chains.\n if (this.flushPromise) {\n this.flushRequested = true;\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushRequested = false;\n const flush = this.flushBatch().finally(() => {\n this.flushPromise = null;\n });\n this.flushPromise = flush;\n // Flushes requested while this request was in flight only got this promise back, so send their logs now,\n // and keep draining full batches. On a transient failure, wait for the next interval instead of retrying\n // straight away. A permanently rejected batch was dropped, so the logs behind it can go out now.\n const continueDraining = () => {\n if (this.logBuffer.length >= this.batchSize || (this.flushRequested && this.logBuffer.length > 0)) {\n this.requestFlush();\n }\n };\n flush.then(continueDraining, error => {\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n continueDraining();\n }\n });\n return flush;\n }\n\n private async flushBatch(): Promise<void> {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n\n try {\n await this.makeHttpRequest(logs);\n this.lastFlush = now;\n } catch (error) {\n // The endpoint rejected this batch outright, so resending it can't succeed and would block the logs behind it.\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n this.droppedLogCount += logs.length;\n console.warn(\n `HttpTransport: endpoint rejected a batch with HTTP ${error.status}; dropping ${logs.length} logs. Use getDroppedLogCount() to track drops.`,\n );\n throw error;\n }\n\n // On a transient error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function): void {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n this.enforceBufferLimit();\n\n // Flush if buffer reaches batch size\n if (this.logBuffer.length >= this.batchSize) {\n this.requestFlush();\n }\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function): void {\n clearInterval(this.flushIntervalId);\n\n // Final drain. Wait out any in-flight request (ignoring its failure, since its logs are back in the buffer),\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n if (this.logBuffer.length > 0 || this.flushPromise) {\n const drain = async () => {\n await this.flushPromise?.catch(() => {});\n while (this.flushPromise || this.logBuffer.length > 0) {\n await (this.flushPromise ?? this._flush());\n }\n };\n\n drain()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogs: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: params?.page === 0 ? 1 : (params?.page ?? 1),\n perPage: params?.perPage ?? 100,\n hasMore: false,\n };\n }\n\n async listLogsByRunId({\n runId: _runId,\n fromDate: _fromDate,\n toDate: _toDate,\n logLevel: _logLevel,\n filters: _filters,\n page,\n perPage,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogsByRunId: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: page === 0 ? 1 : (page ?? 1),\n perPage: perPage ?? 100,\n hasMore: false,\n };\n }\n\n // Utility methods\n public getBufferedLogs(): BaseLogMessage[] {\n return [...this.logBuffer];\n }\n\n public clearBuffer(): void {\n this.logBuffer = [];\n }\n\n public getDroppedLogCount(): number {\n return this.droppedLogCount;\n }\n\n public getLastFlushTime(): number {\n return this.lastFlush;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/http/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\n\ninterface RetryOptions {\n maxRetries?: number;\n retryDelay?: number;\n exponentialBackoff?: boolean;\n}\n\ninterface HttpTransportOptions {\n url: string;\n method?: 'POST' | 'PUT' | 'PATCH';\n headers?: Record<string, string>;\n batchSize?: number;\n flushInterval?: number;\n timeout?: number;\n retryOptions?: RetryOptions;\n maxBufferSize?: number;\n}\n\nclass HttpResponseError extends Error {\n constructor(\n readonly status: number,\n statusText: string,\n readonly retryAfterMs: number | undefined,\n ) {\n super(`HTTP ${status}: ${statusText}`);\n }\n}\n\n// Other 4xx responses mean the request itself is wrong and will fail the same way on every retry.\nfunction isRetryableStatus(status: number): boolean {\n return status === 408 || status === 425 || status === 429 || status >= 500;\n}\n\n// Retry-After is either delta-seconds or an HTTP date (RFC 9110). Invalid values are ignored.\nfunction parseRetryAfter(headers: Headers | undefined): number | undefined {\n const value = headers?.get('retry-after')?.trim();\n if (!value) return undefined;\n if (/^\\d+$/.test(value)) return Number(value) * 1000;\n const date = Date.parse(value);\n return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());\n}\n\nexport class HttpTransport extends LoggerTransport {\n private url: string;\n private method: string;\n private headers: Record<string, string>;\n private batchSize: number;\n private flushInterval: number;\n private timeout: number;\n private retryOptions: Required<RetryOptions>;\n private logBuffer: BaseLogMessage[];\n private maxBufferSize: number;\n private droppedLogCount = 0;\n private flushPromise: Promise<void> | null = null;\n private flushRequested = false;\n private lastFlush: number;\n private flushIntervalId: NodeJS.Timeout;\n\n constructor(options: HttpTransportOptions) {\n super({ objectMode: true });\n\n if (!options.url) {\n throw new Error('HTTP URL is required');\n }\n\n this.url = options.url;\n this.method = options.method || 'POST';\n this.headers = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n this.batchSize = options.batchSize ?? 100;\n this.flushInterval = options.flushInterval || 10000;\n this.timeout = options.timeout || 30000;\n this.retryOptions = {\n maxRetries: options.retryOptions?.maxRetries ?? 3,\n retryDelay: options.retryOptions?.retryDelay ?? 1000,\n exponentialBackoff: options.retryOptions?.exponentialBackoff ?? true,\n };\n\n if (!Number.isInteger(this.batchSize) || this.batchSize < 1) {\n throw new Error('HttpTransport batchSize must be a positive integer');\n }\n\n const maxBufferSize = options.maxBufferSize ?? 10_000;\n if (!Number.isInteger(maxBufferSize) || maxBufferSize < 1) {\n throw new Error('HttpTransport maxBufferSize must be a positive integer');\n }\n this.maxBufferSize = Math.max(maxBufferSize, this.batchSize);\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => this.requestFlush(), this.flushInterval);\n this.updateIntervalRef();\n }\n\n // The interval only keeps the process alive while logs are waiting to be sent, so an idle transport\n // doesn't stop a script from exiting, but buffered logs still get delivered (or retried) first.\n // Web-standard runtimes return a numeric timer id without ref()/unref(), so those calls are optional.\n private updateIntervalRef(): void {\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.flushIntervalId.ref?.();\n } else {\n this.flushIntervalId.unref?.();\n }\n }\n\n private async makeHttpRequest(data: any): Promise<Response> {\n // Serialize once, outside the retry loop: serialization failures are not retryable,\n // and every retry must send the exact same payload.\n const body = JSON.stringify({ logs: data });\n return this.sendWithRetry(body);\n }\n\n private async sendWithRetry(body: string, retryCount = 0): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(this.url, {\n method: this.method,\n headers: this.headers,\n body,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new HttpResponseError(response.status, response.statusText, parseRetryAfter(response.headers));\n }\n\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n\n const isPermanent = error instanceof HttpResponseError && !isRetryableStatus(error.status);\n if (!isPermanent && retryCount < this.retryOptions.maxRetries) {\n const backoff = this.retryOptions.exponentialBackoff\n ? this.retryOptions.retryDelay * Math.pow(2, retryCount)\n : this.retryOptions.retryDelay;\n // Honor the server's Retry-After, capped at the request timeout so a large value can't stall shutdown.\n const retryAfter = error instanceof HttpResponseError ? (error.retryAfterMs ?? 0) : 0;\n const delay = Math.max(backoff, Math.min(retryAfter, this.timeout));\n\n await new Promise(resolve => setTimeout(resolve, delay));\n return this.sendWithRetry(body, retryCount + 1);\n }\n\n throw error;\n }\n }\n\n private enforceBufferLimit(): void {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow <= 0) {\n return;\n }\n\n this.logBuffer.splice(0, overflow);\n if (this.droppedLogCount === 0) {\n console.warn(\n `HttpTransport: buffer exceeded maxBufferSize (${this.maxBufferSize}); dropping oldest logs. Use getDroppedLogCount() to track drops.`,\n );\n }\n this.droppedLogCount += overflow;\n }\n\n // Internal fire-and-forget flush. While a request is in flight this only records the request, so writes\n // during a slow or retrying request don't each attach another handler to the pending promise.\n private requestFlush(): void {\n if (this.flushPromise) {\n this.flushRequested = true;\n return;\n }\n this._flush().catch(err => {\n console.error('Error flushing logs to HTTP endpoint:', err);\n });\n }\n\n _flush(): Promise<void> {\n // Only one request in flight at a time, so an outage doesn't fan out into overlapping retry chains.\n if (this.flushPromise) {\n this.flushRequested = true;\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushRequested = false;\n const flush = this.flushBatch().finally(() => {\n this.flushPromise = null;\n this.updateIntervalRef();\n });\n this.flushPromise = flush;\n // Flushes requested while this request was in flight only got this promise back, so send their logs now,\n // and keep draining full batches. On a transient failure, wait for the next interval instead of retrying\n // straight away. A permanently rejected batch was dropped, so the logs behind it can go out now.\n const continueDraining = () => {\n if (this.logBuffer.length >= this.batchSize || (this.flushRequested && this.logBuffer.length > 0)) {\n this.requestFlush();\n }\n };\n flush.then(continueDraining, error => {\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n continueDraining();\n }\n });\n return flush;\n }\n\n private async flushBatch(): Promise<void> {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n\n try {\n await this.makeHttpRequest(logs);\n this.lastFlush = now;\n } catch (error) {\n // The endpoint rejected this batch outright, so resending it can't succeed and would block the logs behind it.\n if (error instanceof HttpResponseError && !isRetryableStatus(error.status)) {\n this.droppedLogCount += logs.length;\n console.warn(\n `HttpTransport: endpoint rejected a batch with HTTP ${error.status}; dropping ${logs.length} logs. Use getDroppedLogCount() to track drops.`,\n );\n throw error;\n }\n\n // On a transient error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function): void {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n this.enforceBufferLimit();\n this.updateIntervalRef();\n\n // Flush if buffer reaches batch size\n if (this.logBuffer.length >= this.batchSize) {\n this.requestFlush();\n }\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function): void {\n clearInterval(this.flushIntervalId);\n\n // Final drain. Wait out any in-flight request (ignoring its failure, since its logs are back in the buffer),\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n if (this.logBuffer.length > 0 || this.flushPromise) {\n const drain = async () => {\n await this.flushPromise?.catch(() => {});\n while (this.flushPromise || this.logBuffer.length > 0) {\n await (this.flushPromise ?? this._flush());\n }\n };\n\n drain()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogs: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: params?.page === 0 ? 1 : (params?.page ?? 1),\n perPage: params?.perPage ?? 100,\n hasMore: false,\n };\n }\n\n async listLogsByRunId({\n runId: _runId,\n fromDate: _fromDate,\n toDate: _toDate,\n logLevel: _logLevel,\n filters: _filters,\n page,\n perPage,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n // HttpTransport is write-only by default\n // Subclasses can override this method to implement log retrieval\n console.warn(\n 'HttpTransport.listLogsByRunId: This transport is write-only. Override this method to implement log retrieval.',\n );\n\n return {\n logs: [],\n total: 0,\n page: page === 0 ? 1 : (page ?? 1),\n perPage: perPage ?? 100,\n hasMore: false,\n };\n }\n\n // Utility methods\n public getBufferedLogs(): BaseLogMessage[] {\n return [...this.logBuffer];\n }\n\n public clearBuffer(): void {\n this.logBuffer = [];\n this.updateIntervalRef();\n }\n\n public getDroppedLogCount(): number {\n return this.droppedLogCount;\n }\n\n public getLastFlushTime(): number {\n return this.lastFlush;\n }\n}\n"],"mappings":"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"}
@@ -2,6 +2,10 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_pagination = require("../pagination-D4VAcPxN.cjs");
3
3
  let _mastra_core_logger = require("@mastra/core/logger");
4
4
  //#region src/upstash/index.ts
5
+ /** LTRIM failed after LPUSH succeeded: the records were written, so they are not put back in the buffer. */
6
+ var UpstashTrimError = class extends Error {
7
+ name = "UpstashTrimError";
8
+ };
5
9
  var UpstashTransport = class extends _mastra_core_logger.LoggerTransport {
6
10
  upstashUrl;
7
11
  upstashToken;
@@ -34,6 +38,11 @@ var UpstashTransport = class extends _mastra_core_logger.LoggerTransport {
34
38
  console.error("Error flushing logs to Upstash:", err);
35
39
  });
36
40
  }, this.flushInterval);
41
+ this.updateIntervalRef();
42
+ }
43
+ updateIntervalRef() {
44
+ if (this.logBuffer.length > 0 || this.flushPromise) this.flushIntervalId.ref?.();
45
+ else this.flushIntervalId.unref?.();
37
46
  }
38
47
  async executeUpstashCommands(commands) {
39
48
  const response = await fetch(`${this.upstashUrl}/pipeline`, {
@@ -53,32 +62,41 @@ var UpstashTransport = class extends _mastra_core_logger.LoggerTransport {
53
62
  if (this.logBuffer.length === 0) return Promise.resolve();
54
63
  this.flushPromise = this.flushBatch().finally(() => {
55
64
  this.flushPromise = null;
65
+ this.updateIntervalRef();
56
66
  });
57
67
  return this.flushPromise;
58
68
  }
59
- /** Pushes one batch to Upstash, returning the records to the front of the buffer if the request fails. */
69
+ /**
70
+ * Pushes one batch to Upstash. If the request or the LPUSH command fails, the records return to the
71
+ * front of the buffer. A failed LTRIM is reported without resending, since the records were already written.
72
+ */
60
73
  async flushBatch() {
61
74
  const now = Date.now();
62
75
  const logs = this.logBuffer.splice(0, this.batchSize);
76
+ const commands = [[
77
+ "LPUSH",
78
+ this.listName,
79
+ ...logs.map((log) => JSON.stringify(log))
80
+ ]];
81
+ if (this.maxListLength > 0) commands.push([
82
+ "LTRIM",
83
+ this.listName,
84
+ 0,
85
+ this.maxListLength - 1
86
+ ]);
87
+ let results;
63
88
  try {
64
- const commands = [[
65
- "LPUSH",
66
- this.listName,
67
- ...logs.map((log) => JSON.stringify(log))
68
- ]];
69
- if (this.maxListLength > 0) commands.push([
70
- "LTRIM",
71
- this.listName,
72
- 0,
73
- this.maxListLength - 1
74
- ]);
75
- await this.executeUpstashCommands(commands);
76
- this.lastFlush = now;
89
+ results = await this.executeUpstashCommands(commands);
90
+ const pushError = Array.isArray(results) ? results[0]?.error : void 0;
91
+ if (pushError) throw new Error(`Failed to push logs to Upstash: ${pushError}`);
77
92
  } catch (error) {
78
93
  this.logBuffer.unshift(...logs);
79
94
  this.enforceBufferLimit();
80
95
  throw error;
81
96
  }
97
+ this.lastFlush = now;
98
+ const trimError = Array.isArray(results) ? results[1]?.error : void 0;
99
+ if (trimError) throw new UpstashTrimError(`Failed to trim Upstash log list: ${trimError}`);
82
100
  }
83
101
  /** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */
84
102
  enforceBufferLimit() {
@@ -106,6 +124,7 @@ var UpstashTransport = class extends _mastra_core_logger.LoggerTransport {
106
124
  time: Date.now()
107
125
  } : parsed;
108
126
  this.logBuffer.push(log);
127
+ this.updateIntervalRef();
109
128
  if (this.logBuffer.length >= Math.min(this.batchSize, this.maxBufferSize)) this._flush().catch((err) => {
110
129
  console.error("Error flushing logs to Upstash:", err);
111
130
  });
@@ -124,8 +143,17 @@ var UpstashTransport = class extends _mastra_core_logger.LoggerTransport {
124
143
  else cb(err);
125
144
  }
126
145
  async drainBuffer() {
127
- await this.flushPromise?.catch(() => {});
128
- while (this.logBuffer.length > 0 || this.flushPromise) await (this.flushPromise ?? this._flush());
146
+ let trimError;
147
+ await this.flushPromise?.catch((error) => {
148
+ if (error instanceof UpstashTrimError) trimError ??= error;
149
+ });
150
+ while (this.logBuffer.length > 0 || this.flushPromise) try {
151
+ await (this.flushPromise ?? this._flush());
152
+ } catch (error) {
153
+ if (!(error instanceof UpstashTrimError)) throw error;
154
+ trimError ??= error;
155
+ }
156
+ if (trimError) throw trimError;
129
157
  }
130
158
  parseLogs(logs) {
131
159
  if (!Array.isArray(logs)) return [];
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["LoggerTransport","normalizePagination"],"sources":["../../src/upstash/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\nimport { normalizePagination } from '../pagination';\n\nexport class UpstashTransport extends LoggerTransport {\n upstashUrl: string;\n upstashToken: string;\n listName: string;\n maxListLength: number;\n maxBufferSize: number;\n /** Number of buffered records discarded because the buffer exceeded `maxBufferSize`. */\n droppedRecords = 0;\n batchSize: number;\n flushInterval: number;\n logBuffer: any[];\n lastFlush: number;\n flushIntervalId: NodeJS.Timeout;\n private flushPromise: Promise<void> | null = null;\n\n constructor(opts: {\n listName?: string;\n maxListLength?: number;\n /** Maximum number of records held in memory while Upstash is unreachable. Must be a positive integer. Defaults to `maxListLength`, or 10000 when `maxListLength` disables trimming. */\n maxBufferSize?: number;\n batchSize?: number;\n upstashUrl: string;\n flushInterval?: number;\n upstashToken: string;\n }) {\n super({ objectMode: true });\n\n if (!opts.upstashUrl || !opts.upstashToken) {\n throw new Error('Upstash URL and token are required');\n }\n\n this.upstashUrl = opts.upstashUrl;\n this.upstashToken = opts.upstashToken;\n this.listName = opts.listName || 'application-logs';\n this.maxListLength = opts.maxListLength || 10000;\n // A non-positive maxListLength disables LTRIM, so it cannot serve as the buffer limit.\n this.maxBufferSize = opts.maxBufferSize ?? (this.maxListLength > 0 ? this.maxListLength : 10000);\n if (!Number.isInteger(this.maxBufferSize) || this.maxBufferSize < 1) {\n throw new Error('maxBufferSize must be a positive integer');\n }\n this.batchSize = opts.batchSize || 100;\n this.flushInterval = opts.flushInterval || 10000;\n\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }, this.flushInterval);\n }\n\n private async executeUpstashCommands(commands: any[][]): Promise<any> {\n const response = await fetch(`${this.upstashUrl}/pipeline`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.upstashToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(commands),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to execute Upstash command: ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /** Sends the next batch to Upstash. Callers share the in-flight flush instead of starting a new request. */\n _flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushPromise = this.flushBatch().finally(() => {\n this.flushPromise = null;\n });\n return this.flushPromise;\n }\n\n /** Pushes one batch to Upstash, returning the records to the front of the buffer if the request fails. */\n private async flushBatch() {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n\n try {\n const commands: any[][] = [['LPUSH', this.listName, ...logs.map(log => JSON.stringify(log))]];\n\n // Trim the list if it exceeds maxListLength (must be a separate pipeline command)\n if (this.maxListLength > 0) {\n commands.push(['LTRIM', this.listName, 0, this.maxListLength - 1]);\n }\n\n await this.executeUpstashCommands(commands);\n this.lastFlush = now;\n } catch (error) {\n // On error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n }\n\n /** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */\n private enforceBufferLimit() {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow > 0) {\n this.logBuffer.splice(0, overflow);\n this.droppedRecords += overflow;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function) {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n\n // Flush once a batch is ready, or earlier if the buffer limit is smaller than a batch\n if (this.logBuffer.length >= Math.min(this.batchSize, this.maxBufferSize)) {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }\n\n this.enforceBufferLimit();\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function) {\n clearInterval(this.flushIntervalId);\n\n // Final flush\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.drainBuffer()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n // Wait out any in-flight request, ignoring its failure since its records are back in the buffer,\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n private async drainBuffer(): Promise<void> {\n await this.flushPromise?.catch(() => {});\n while (this.logBuffer.length > 0 || this.flushPromise) {\n await (this.flushPromise ?? this._flush());\n }\n }\n\n private parseLogs(logs: unknown): BaseLogMessage[] {\n if (!Array.isArray(logs)) return [];\n\n return logs.flatMap((log: string) => {\n try {\n const parsedLog = JSON.parse(log);\n return parsedLog !== null && typeof parsedLog === 'object' ? [parsedLog] : [];\n } catch {\n return [];\n }\n });\n }\n\n private matchesLog(\n log: BaseLogMessage,\n {\n fromDate,\n toDate,\n logLevel,\n filters,\n runId,\n }: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n runId?: string;\n },\n ): boolean {\n if (runId !== undefined && log.runId !== runId) return false;\n if (logLevel && log.level !== logLevel) return false;\n const logTime = new Date(log.time).getTime();\n if (fromDate && !(logTime >= fromDate.getTime())) return false;\n if (toDate && !(logTime <= toDate.getTime())) return false;\n\n return !filters || Object.entries(filters).every(([key, value]) => log[key as keyof BaseLogMessage] === value);\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean; // default true\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(params?.page, params?.perPage);\n try {\n const {\n fromDate,\n toDate,\n logLevel,\n filters,\n returnPaginationResults: returnPaginationResultsInput,\n } = params || {};\n\n const returnPaginationResults = returnPaginationResultsInput ?? true;\n const hasFilters = Boolean(fromDate || toDate || logLevel || (filters && Object.keys(filters).length > 0));\n\n if (returnPaginationResults && !hasFilters) {\n const response = await this.executeUpstashCommands([\n ['LLEN', this.listName],\n ['LRANGE', this.listName, start, end - 1],\n ]);\n const total = Number(response?.[0]?.result) || 0;\n const logs = this.parseLogs(response?.[1]?.result);\n\n return {\n logs,\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n }\n\n const response = await this.executeUpstashCommands([['LRANGE', this.listName, 0, -1]]);\n const filteredLogs = this.parseLogs(response?.[0]?.result).filter(log =>\n this.matchesLog(log, { fromDate, toDate, logLevel, filters }),\n );\n\n if (!returnPaginationResults) {\n return {\n logs: filteredLogs,\n total: filteredLogs.length,\n page,\n perPage: filteredLogs.length,\n hasMore: false,\n };\n }\n\n const total = filteredLogs.length;\n\n return {\n logs: filteredLogs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n\n async listLogsByRunId({\n runId,\n fromDate,\n toDate,\n logLevel,\n filters,\n page: pageInput,\n perPage: perPageInput,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(pageInput, perPageInput);\n try {\n const allLogs = await this.listLogs({ fromDate, toDate, logLevel, filters, returnPaginationResults: false });\n const logs = allLogs.logs.filter(log => this.matchesLog(log, { runId }));\n const total = logs.length;\n\n return {\n logs: logs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs by runId from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["LoggerTransport","normalizePagination"],"sources":["../../src/upstash/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\nimport { normalizePagination } from '../pagination';\n\n/** LTRIM failed after LPUSH succeeded: the records were written, so they are not put back in the buffer. */\nclass UpstashTrimError extends Error {\n name = 'UpstashTrimError';\n}\n\nexport class UpstashTransport extends LoggerTransport {\n upstashUrl: string;\n upstashToken: string;\n listName: string;\n maxListLength: number;\n maxBufferSize: number;\n /** Number of buffered records discarded because the buffer exceeded `maxBufferSize`. */\n droppedRecords = 0;\n batchSize: number;\n flushInterval: number;\n logBuffer: any[];\n lastFlush: number;\n flushIntervalId: NodeJS.Timeout;\n private flushPromise: Promise<void> | null = null;\n\n constructor(opts: {\n listName?: string;\n maxListLength?: number;\n /** Maximum number of records held in memory while Upstash is unreachable. Must be a positive integer. Defaults to `maxListLength`, or 10000 when `maxListLength` disables trimming. */\n maxBufferSize?: number;\n batchSize?: number;\n upstashUrl: string;\n flushInterval?: number;\n upstashToken: string;\n }) {\n super({ objectMode: true });\n\n if (!opts.upstashUrl || !opts.upstashToken) {\n throw new Error('Upstash URL and token are required');\n }\n\n this.upstashUrl = opts.upstashUrl;\n this.upstashToken = opts.upstashToken;\n this.listName = opts.listName || 'application-logs';\n this.maxListLength = opts.maxListLength || 10000;\n // A non-positive maxListLength disables LTRIM, so it cannot serve as the buffer limit.\n this.maxBufferSize = opts.maxBufferSize ?? (this.maxListLength > 0 ? this.maxListLength : 10000);\n if (!Number.isInteger(this.maxBufferSize) || this.maxBufferSize < 1) {\n throw new Error('maxBufferSize must be a positive integer');\n }\n this.batchSize = opts.batchSize || 100;\n this.flushInterval = opts.flushInterval || 10000;\n\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }, this.flushInterval);\n this.updateIntervalRef();\n }\n\n // The interval only keeps the process alive while records are waiting to be sent, so an idle transport\n // doesn't stop a script from exiting, but buffered records still get delivered (or retried) first.\n // Web-standard runtimes return a numeric timer id without ref()/unref(), so those calls are optional.\n private updateIntervalRef() {\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.flushIntervalId.ref?.();\n } else {\n this.flushIntervalId.unref?.();\n }\n }\n\n private async executeUpstashCommands(commands: any[][]): Promise<any> {\n const response = await fetch(`${this.upstashUrl}/pipeline`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.upstashToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(commands),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to execute Upstash command: ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /** Sends the next batch to Upstash. Callers share the in-flight flush instead of starting a new request. */\n _flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushPromise = this.flushBatch().finally(() => {\n this.flushPromise = null;\n this.updateIntervalRef();\n });\n return this.flushPromise;\n }\n\n /**\n * Pushes one batch to Upstash. If the request or the LPUSH command fails, the records return to the\n * front of the buffer. A failed LTRIM is reported without resending, since the records were already written.\n */\n private async flushBatch() {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n const commands: any[][] = [['LPUSH', this.listName, ...logs.map(log => JSON.stringify(log))]];\n\n // Trim the list if it exceeds maxListLength (must be a separate pipeline command)\n if (this.maxListLength > 0) {\n commands.push(['LTRIM', this.listName, 0, this.maxListLength - 1]);\n }\n\n let results: any;\n try {\n results = await this.executeUpstashCommands(commands);\n // Upstash answers HTTP 200 even when a command fails, reporting { error } per command\n const pushError = Array.isArray(results) ? results[0]?.error : undefined;\n if (pushError) {\n throw new Error(`Failed to push logs to Upstash: ${pushError}`);\n }\n } catch (error) {\n // On error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n\n this.lastFlush = now;\n const trimError = Array.isArray(results) ? results[1]?.error : undefined;\n if (trimError) {\n throw new UpstashTrimError(`Failed to trim Upstash log list: ${trimError}`);\n }\n }\n\n /** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */\n private enforceBufferLimit() {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow > 0) {\n this.logBuffer.splice(0, overflow);\n this.droppedRecords += overflow;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function) {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n this.updateIntervalRef();\n\n // Flush once a batch is ready, or earlier if the buffer limit is smaller than a batch\n if (this.logBuffer.length >= Math.min(this.batchSize, this.maxBufferSize)) {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }\n\n this.enforceBufferLimit();\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function) {\n clearInterval(this.flushIntervalId);\n\n // Final flush\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.drainBuffer()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n // Wait out any in-flight request, ignoring its failure since its records are back in the buffer,\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n // A trim failure leaves nothing to resend, so draining continues and the first one is reported at the end.\n private async drainBuffer(): Promise<void> {\n let trimError: UpstashTrimError | undefined;\n await this.flushPromise?.catch(error => {\n if (error instanceof UpstashTrimError) trimError ??= error;\n });\n while (this.logBuffer.length > 0 || this.flushPromise) {\n try {\n await (this.flushPromise ?? this._flush());\n } catch (error) {\n if (!(error instanceof UpstashTrimError)) throw error;\n trimError ??= error;\n }\n }\n if (trimError) throw trimError;\n }\n\n private parseLogs(logs: unknown): BaseLogMessage[] {\n if (!Array.isArray(logs)) return [];\n\n return logs.flatMap((log: string) => {\n try {\n const parsedLog = JSON.parse(log);\n return parsedLog !== null && typeof parsedLog === 'object' ? [parsedLog] : [];\n } catch {\n return [];\n }\n });\n }\n\n private matchesLog(\n log: BaseLogMessage,\n {\n fromDate,\n toDate,\n logLevel,\n filters,\n runId,\n }: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n runId?: string;\n },\n ): boolean {\n if (runId !== undefined && log.runId !== runId) return false;\n if (logLevel && log.level !== logLevel) return false;\n const logTime = new Date(log.time).getTime();\n if (fromDate && !(logTime >= fromDate.getTime())) return false;\n if (toDate && !(logTime <= toDate.getTime())) return false;\n\n return !filters || Object.entries(filters).every(([key, value]) => log[key as keyof BaseLogMessage] === value);\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean; // default true\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(params?.page, params?.perPage);\n try {\n const {\n fromDate,\n toDate,\n logLevel,\n filters,\n returnPaginationResults: returnPaginationResultsInput,\n } = params || {};\n\n const returnPaginationResults = returnPaginationResultsInput ?? true;\n const hasFilters = Boolean(fromDate || toDate || logLevel || (filters && Object.keys(filters).length > 0));\n\n if (returnPaginationResults && !hasFilters) {\n const response = await this.executeUpstashCommands([\n ['LLEN', this.listName],\n ['LRANGE', this.listName, start, end - 1],\n ]);\n const total = Number(response?.[0]?.result) || 0;\n const logs = this.parseLogs(response?.[1]?.result);\n\n return {\n logs,\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n }\n\n const response = await this.executeUpstashCommands([['LRANGE', this.listName, 0, -1]]);\n const filteredLogs = this.parseLogs(response?.[0]?.result).filter(log =>\n this.matchesLog(log, { fromDate, toDate, logLevel, filters }),\n );\n\n if (!returnPaginationResults) {\n return {\n logs: filteredLogs,\n total: filteredLogs.length,\n page,\n perPage: filteredLogs.length,\n hasMore: false,\n };\n }\n\n const total = filteredLogs.length;\n\n return {\n logs: filteredLogs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n\n async listLogsByRunId({\n runId,\n fromDate,\n toDate,\n logLevel,\n filters,\n page: pageInput,\n perPage: perPageInput,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(pageInput, perPageInput);\n try {\n const allLogs = await this.listLogs({ fromDate, toDate, logLevel, filters, returnPaginationResults: false });\n const logs = allLogs.logs.filter(log => this.matchesLog(log, { runId }));\n const total = logs.length;\n\n return {\n logs: logs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs by runId from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n}\n"],"mappings":";;;;;AAKA,IAAM,mBAAN,cAA+B,MAAM;CACnC,OAAO;AACT;AAEA,IAAa,mBAAb,cAAsCA,oBAAAA,gBAAgB;CACpD;CACA;CACA;CACA;CACA;;CAEA,iBAAiB;CACjB;CACA;CACA;CACA;CACA;CACA,eAA6C;CAE7C,YAAY,MAST;EACD,MAAM,EAAE,YAAY,KAAK,CAAC;EAE1B,IAAI,CAAC,KAAK,cAAc,CAAC,KAAK,cAC5B,MAAM,IAAI,MAAM,oCAAoC;EAGtD,KAAK,aAAa,KAAK;EACvB,KAAK,eAAe,KAAK;EACzB,KAAK,WAAW,KAAK,YAAY;EACjC,KAAK,gBAAgB,KAAK,iBAAiB;EAE3C,KAAK,gBAAgB,KAAK,kBAAkB,KAAK,gBAAgB,IAAI,KAAK,gBAAgB;EAC1F,IAAI,CAAC,OAAO,UAAU,KAAK,aAAa,KAAK,KAAK,gBAAgB,GAChE,MAAM,IAAI,MAAM,0CAA0C;EAE5D,KAAK,YAAY,KAAK,aAAa;EACnC,KAAK,gBAAgB,KAAK,iBAAiB;EAE3C,KAAK,YAAY,CAAC;EAClB,KAAK,YAAY,KAAK,IAAI;EAG1B,KAAK,kBAAkB,kBAAkB;GACvC,KAAK,OAAO,CAAC,CAAC,OAAM,QAAO;IACzB,QAAQ,MAAM,mCAAmC,GAAG;GACtD,CAAC;EACH,GAAG,KAAK,aAAa;EACrB,KAAK,kBAAkB;CACzB;CAKA,oBAA4B;EAC1B,IAAI,KAAK,UAAU,SAAS,KAAK,KAAK,cACpC,KAAK,gBAAgB,MAAM;OAE3B,KAAK,gBAAgB,QAAQ;CAEjC;CAEA,MAAc,uBAAuB,UAAiC;EACpE,MAAM,WAAW,MAAM,MAAM,GAAG,KAAK,WAAW,YAAY;GAC1D,QAAQ;GACR,SAAS;IACP,eAAe,UAAU,KAAK;IAC9B,gBAAgB;GAClB;GACA,MAAM,KAAK,UAAU,QAAQ;EAC/B,CAAC;EAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,sCAAsC,SAAS,YAAY;EAG7E,OAAO,SAAS,KAAK;CACvB;;CAGA,SAAwB;EACtB,IAAI,KAAK,cACP,OAAO,KAAK;EAEd,IAAI,KAAK,UAAU,WAAW,GAC5B,OAAO,QAAQ,QAAQ;EAGzB,KAAK,eAAe,KAAK,WAAW,CAAC,CAAC,cAAc;GAClD,KAAK,eAAe;GACpB,KAAK,kBAAkB;EACzB,CAAC;EACD,OAAO,KAAK;CACd;;;;;CAMA,MAAc,aAAa;EACzB,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,OAAO,KAAK,UAAU,OAAO,GAAG,KAAK,SAAS;EACpD,MAAM,WAAoB,CAAC;GAAC;GAAS,KAAK;GAAU,GAAG,KAAK,KAAI,QAAO,KAAK,UAAU,GAAG,CAAC;EAAC,CAAC;EAG5F,IAAI,KAAK,gBAAgB,GACvB,SAAS,KAAK;GAAC;GAAS,KAAK;GAAU;GAAG,KAAK,gBAAgB;EAAC,CAAC;EAGnE,IAAI;EACJ,IAAI;GACF,UAAU,MAAM,KAAK,uBAAuB,QAAQ;GAEpD,MAAM,YAAY,MAAM,QAAQ,OAAO,IAAI,QAAQ,EAAE,EAAE,QAAQ,KAAA;GAC/D,IAAI,WACF,MAAM,IAAI,MAAM,mCAAmC,WAAW;EAElE,SAAS,OAAO;GAEd,KAAK,UAAU,QAAQ,GAAG,IAAI;GAC9B,KAAK,mBAAmB;GACxB,MAAM;EACR;EAEA,KAAK,YAAY;EACjB,MAAM,YAAY,MAAM,QAAQ,OAAO,IAAI,QAAQ,EAAE,EAAE,QAAQ,KAAA;EAC/D,IAAI,WACF,MAAM,IAAI,iBAAiB,oCAAoC,WAAW;CAE9E;;CAGA,qBAA6B;EAC3B,MAAM,WAAW,KAAK,UAAU,SAAS,KAAK;EAC9C,IAAI,WAAW,GAAG;GAChB,KAAK,UAAU,OAAO,GAAG,QAAQ;GACjC,KAAK,kBAAkB;EACzB;CACF;CAEA,OAAO,OAAY,UAAmB,UAAoD;EACxF,IAAI,OAAO,aAAa,YAAY;GAClC,KAAK,WAAW,OAAO,YAAY,QAAQ,QAAQ;GACnD,OAAO;EACT;EAEA,KAAK,WAAW,OAAO,YAAY,SAAS,UAAwB;GAClE,IAAI,OAAO,QAAQ,MAAM,6BAA6B,KAAK;EAC7D,CAAC;EACD,OAAO;CACT;CAEA,WAAW,OAAe,MAAc,IAAc;EACpD,IAAI;GAEF,MAAM,SAAS,OAAO,UAAU,WAAW,KAAK,MAAM,KAAK,IAAI;GAG/D,MAAM,MAAM,OAAO,SAAS,KAAA,KAAa,OAAO,SAAS,OAAO;IAAE,GAAG;IAAQ,MAAM,KAAK,IAAI;GAAE,IAAI;GAGlG,KAAK,UAAU,KAAK,GAAG;GACvB,KAAK,kBAAkB;GAGvB,IAAI,KAAK,UAAU,UAAU,KAAK,IAAI,KAAK,WAAW,KAAK,aAAa,GACtE,KAAK,OAAO,CAAC,CAAC,OAAM,QAAO;IACzB,QAAQ,MAAM,mCAAmC,GAAG;GACtD,CAAC;GAGH,KAAK,mBAAmB;GAGxB,GAAG,MAAM,KAAK;EAChB,SAAS,OAAO;GACd,GAAG,KAAK;EACV;CACF;CAEA,SAAS,KAAY,IAAc;EACjC,cAAc,KAAK,eAAe;EAGlC,IAAI,KAAK,UAAU,SAAS,KAAK,KAAK,cACpC,KAAK,YAAY,CAAC,CACf,WAAW,GAAG,GAAG,CAAC,CAAC,CACnB,OAAM,aAAY;GACjB,QAAQ,MAAM,yBAAyB,QAAQ;GAC/C,GAAG,OAAO,QAAQ;EACpB,CAAC;OAEH,GAAG,GAAG;CAEV;CAKA,MAAc,cAA6B;EACzC,IAAI;EACJ,MAAM,KAAK,cAAc,OAAM,UAAS;GACtC,IAAI,iBAAiB,kBAAkB,cAAc;EACvD,CAAC;EACD,OAAO,KAAK,UAAU,SAAS,KAAK,KAAK,cACvC,IAAI;GACF,OAAO,KAAK,gBAAgB,KAAK,OAAO;EAC1C,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,mBAAmB,MAAM;GAChD,cAAc;EAChB;EAEF,IAAI,WAAW,MAAM;CACvB;CAEA,UAAkB,MAAiC;EACjD,IAAI,CAAC,MAAM,QAAQ,IAAI,GAAG,OAAO,CAAC;EAElC,OAAO,KAAK,SAAS,QAAgB;GACnC,IAAI;IACF,MAAM,YAAY,KAAK,MAAM,GAAG;IAChC,OAAO,cAAc,QAAQ,OAAO,cAAc,WAAW,CAAC,SAAS,IAAI,CAAC;GAC9E,QAAQ;IACN,OAAO,CAAC;GACV;EACF,CAAC;CACH;CAEA,WACE,KACA,EACE,UACA,QACA,UACA,SACA,SAQO;EACT,IAAI,UAAU,KAAA,KAAa,IAAI,UAAU,OAAO,OAAO;EACvD,IAAI,YAAY,IAAI,UAAU,UAAU,OAAO;EAC/C,MAAM,UAAU,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ;EAC3C,IAAI,YAAY,EAAE,WAAW,SAAS,QAAQ,IAAI,OAAO;EACzD,IAAI,UAAU,EAAE,WAAW,OAAO,QAAQ,IAAI,OAAO;EAErD,OAAO,CAAC,WAAW,OAAO,QAAQ,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,WAAW,IAAI,SAAiC,KAAK;CAC/G;CAEA,MAAM,SAAS,QAcZ;EACD,MAAM,EAAE,MAAM,SAAS,OAAO,QAAQC,mBAAAA,oBAAoB,QAAQ,MAAM,QAAQ,OAAO;EACvF,IAAI;GACF,MAAM,EACJ,UACA,QACA,UACA,SACA,yBAAyB,iCACvB,UAAU,CAAC;GAEf,MAAM,0BAA0B,gCAAgC;GAChE,MAAM,aAAa,QAAQ,YAAY,UAAU,YAAa,WAAW,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,CAAE;GAEzG,IAAI,2BAA2B,CAAC,YAAY;IAC1C,MAAM,WAAW,MAAM,KAAK,uBAAuB,CACjD,CAAC,QAAQ,KAAK,QAAQ,GACtB;KAAC;KAAU,KAAK;KAAU;KAAO,MAAM;IAAC,CAC1C,CAAC;IACD,MAAM,QAAQ,OAAO,WAAW,EAAE,EAAE,MAAM,KAAK;IAG/C,OAAO;KACL,MAHW,KAAK,UAAU,WAAW,EAAE,EAAE,MAGtC;KACH;KACA;KACA;KACA,SAAS,MAAM;IACjB;GACF;GAEA,MAAM,WAAW,MAAM,KAAK,uBAAuB,CAAC;IAAC;IAAU,KAAK;IAAU;IAAG;GAAE,CAAC,CAAC;GACrF,MAAM,eAAe,KAAK,UAAU,WAAW,EAAE,EAAE,MAAM,CAAC,CAAC,QAAO,QAChE,KAAK,WAAW,KAAK;IAAE;IAAU;IAAQ;IAAU;GAAQ,CAAC,CAC9D;GAEA,IAAI,CAAC,yBACH,OAAO;IACL,MAAM;IACN,OAAO,aAAa;IACpB;IACA,SAAS,aAAa;IACtB,SAAS;GACX;GAGF,MAAM,QAAQ,aAAa;GAE3B,OAAO;IACL,MAAM,aAAa,MAAM,OAAO,GAAG;IACnC;IACA;IACA;IACA,SAAS,MAAM;GACjB;EACF,SAAS,OAAO;GACd,QAAQ,MAAM,oCAAoC,KAAK;GACvD,OAAO;IACL,MAAM,CAAC;IACP,OAAO;IACP;IACA;IACA,SAAS;GACX;EACF;CACF;CAEA,MAAM,gBAAgB,EACpB,OACA,UACA,QACA,UACA,SACA,MAAM,WACN,SAAS,gBAeR;EACD,MAAM,EAAE,MAAM,SAAS,OAAO,QAAQA,mBAAAA,oBAAoB,WAAW,YAAY;EACjF,IAAI;GAEF,MAAM,QAAO,MADS,KAAK,SAAS;IAAE;IAAU;IAAQ;IAAU;IAAS,yBAAyB;GAAM,CAAC,EAAA,CACtF,KAAK,QAAO,QAAO,KAAK,WAAW,KAAK,EAAE,MAAM,CAAC,CAAC;GACvE,MAAM,QAAQ,KAAK;GAEnB,OAAO;IACL,MAAM,KAAK,MAAM,OAAO,GAAG;IAC3B;IACA;IACA;IACA,SAAS,MAAM;GACjB;EACF,SAAS,OAAO;GACd,QAAQ,MAAM,6CAA6C,KAAK;GAChE,OAAO;IACL,MAAM,CAAC;IACP,OAAO;IACP;IACA;IACA,SAAS;GACX;EACF;CACF;AACF"}
@@ -24,10 +24,14 @@ export declare class UpstashTransport extends LoggerTransport {
24
24
  flushInterval?: number;
25
25
  upstashToken: string;
26
26
  });
27
+ private updateIntervalRef;
27
28
  private executeUpstashCommands;
28
29
  /** Sends the next batch to Upstash. Callers share the in-flight flush instead of starting a new request. */
29
30
  _flush(): Promise<void>;
30
- /** Pushes one batch to Upstash, returning the records to the front of the buffer if the request fails. */
31
+ /**
32
+ * Pushes one batch to Upstash. If the request or the LPUSH command fails, the records return to the
33
+ * front of the buffer. A failed LTRIM is reported without resending, since the records were already written.
34
+ */
31
35
  private flushBatch;
32
36
  /** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */
33
37
  private enforceBufferLimit;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/upstash/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/upstash/index.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,10 @@
1
1
  import { t as normalizePagination } from "../pagination-BfSC4BZ-.js";
2
2
  import { LoggerTransport } from "@mastra/core/logger";
3
3
  //#region src/upstash/index.ts
4
+ /** LTRIM failed after LPUSH succeeded: the records were written, so they are not put back in the buffer. */
5
+ var UpstashTrimError = class extends Error {
6
+ name = "UpstashTrimError";
7
+ };
4
8
  var UpstashTransport = class extends LoggerTransport {
5
9
  upstashUrl;
6
10
  upstashToken;
@@ -33,6 +37,11 @@ var UpstashTransport = class extends LoggerTransport {
33
37
  console.error("Error flushing logs to Upstash:", err);
34
38
  });
35
39
  }, this.flushInterval);
40
+ this.updateIntervalRef();
41
+ }
42
+ updateIntervalRef() {
43
+ if (this.logBuffer.length > 0 || this.flushPromise) this.flushIntervalId.ref?.();
44
+ else this.flushIntervalId.unref?.();
36
45
  }
37
46
  async executeUpstashCommands(commands) {
38
47
  const response = await fetch(`${this.upstashUrl}/pipeline`, {
@@ -52,32 +61,41 @@ var UpstashTransport = class extends LoggerTransport {
52
61
  if (this.logBuffer.length === 0) return Promise.resolve();
53
62
  this.flushPromise = this.flushBatch().finally(() => {
54
63
  this.flushPromise = null;
64
+ this.updateIntervalRef();
55
65
  });
56
66
  return this.flushPromise;
57
67
  }
58
- /** Pushes one batch to Upstash, returning the records to the front of the buffer if the request fails. */
68
+ /**
69
+ * Pushes one batch to Upstash. If the request or the LPUSH command fails, the records return to the
70
+ * front of the buffer. A failed LTRIM is reported without resending, since the records were already written.
71
+ */
59
72
  async flushBatch() {
60
73
  const now = Date.now();
61
74
  const logs = this.logBuffer.splice(0, this.batchSize);
75
+ const commands = [[
76
+ "LPUSH",
77
+ this.listName,
78
+ ...logs.map((log) => JSON.stringify(log))
79
+ ]];
80
+ if (this.maxListLength > 0) commands.push([
81
+ "LTRIM",
82
+ this.listName,
83
+ 0,
84
+ this.maxListLength - 1
85
+ ]);
86
+ let results;
62
87
  try {
63
- const commands = [[
64
- "LPUSH",
65
- this.listName,
66
- ...logs.map((log) => JSON.stringify(log))
67
- ]];
68
- if (this.maxListLength > 0) commands.push([
69
- "LTRIM",
70
- this.listName,
71
- 0,
72
- this.maxListLength - 1
73
- ]);
74
- await this.executeUpstashCommands(commands);
75
- this.lastFlush = now;
88
+ results = await this.executeUpstashCommands(commands);
89
+ const pushError = Array.isArray(results) ? results[0]?.error : void 0;
90
+ if (pushError) throw new Error(`Failed to push logs to Upstash: ${pushError}`);
76
91
  } catch (error) {
77
92
  this.logBuffer.unshift(...logs);
78
93
  this.enforceBufferLimit();
79
94
  throw error;
80
95
  }
96
+ this.lastFlush = now;
97
+ const trimError = Array.isArray(results) ? results[1]?.error : void 0;
98
+ if (trimError) throw new UpstashTrimError(`Failed to trim Upstash log list: ${trimError}`);
81
99
  }
82
100
  /** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */
83
101
  enforceBufferLimit() {
@@ -105,6 +123,7 @@ var UpstashTransport = class extends LoggerTransport {
105
123
  time: Date.now()
106
124
  } : parsed;
107
125
  this.logBuffer.push(log);
126
+ this.updateIntervalRef();
108
127
  if (this.logBuffer.length >= Math.min(this.batchSize, this.maxBufferSize)) this._flush().catch((err) => {
109
128
  console.error("Error flushing logs to Upstash:", err);
110
129
  });
@@ -123,8 +142,17 @@ var UpstashTransport = class extends LoggerTransport {
123
142
  else cb(err);
124
143
  }
125
144
  async drainBuffer() {
126
- await this.flushPromise?.catch(() => {});
127
- while (this.logBuffer.length > 0 || this.flushPromise) await (this.flushPromise ?? this._flush());
145
+ let trimError;
146
+ await this.flushPromise?.catch((error) => {
147
+ if (error instanceof UpstashTrimError) trimError ??= error;
148
+ });
149
+ while (this.logBuffer.length > 0 || this.flushPromise) try {
150
+ await (this.flushPromise ?? this._flush());
151
+ } catch (error) {
152
+ if (!(error instanceof UpstashTrimError)) throw error;
153
+ trimError ??= error;
154
+ }
155
+ if (trimError) throw trimError;
128
156
  }
129
157
  parseLogs(logs) {
130
158
  if (!Array.isArray(logs)) return [];
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/upstash/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\nimport { normalizePagination } from '../pagination';\n\nexport class UpstashTransport extends LoggerTransport {\n upstashUrl: string;\n upstashToken: string;\n listName: string;\n maxListLength: number;\n maxBufferSize: number;\n /** Number of buffered records discarded because the buffer exceeded `maxBufferSize`. */\n droppedRecords = 0;\n batchSize: number;\n flushInterval: number;\n logBuffer: any[];\n lastFlush: number;\n flushIntervalId: NodeJS.Timeout;\n private flushPromise: Promise<void> | null = null;\n\n constructor(opts: {\n listName?: string;\n maxListLength?: number;\n /** Maximum number of records held in memory while Upstash is unreachable. Must be a positive integer. Defaults to `maxListLength`, or 10000 when `maxListLength` disables trimming. */\n maxBufferSize?: number;\n batchSize?: number;\n upstashUrl: string;\n flushInterval?: number;\n upstashToken: string;\n }) {\n super({ objectMode: true });\n\n if (!opts.upstashUrl || !opts.upstashToken) {\n throw new Error('Upstash URL and token are required');\n }\n\n this.upstashUrl = opts.upstashUrl;\n this.upstashToken = opts.upstashToken;\n this.listName = opts.listName || 'application-logs';\n this.maxListLength = opts.maxListLength || 10000;\n // A non-positive maxListLength disables LTRIM, so it cannot serve as the buffer limit.\n this.maxBufferSize = opts.maxBufferSize ?? (this.maxListLength > 0 ? this.maxListLength : 10000);\n if (!Number.isInteger(this.maxBufferSize) || this.maxBufferSize < 1) {\n throw new Error('maxBufferSize must be a positive integer');\n }\n this.batchSize = opts.batchSize || 100;\n this.flushInterval = opts.flushInterval || 10000;\n\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }, this.flushInterval);\n }\n\n private async executeUpstashCommands(commands: any[][]): Promise<any> {\n const response = await fetch(`${this.upstashUrl}/pipeline`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.upstashToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(commands),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to execute Upstash command: ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /** Sends the next batch to Upstash. Callers share the in-flight flush instead of starting a new request. */\n _flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushPromise = this.flushBatch().finally(() => {\n this.flushPromise = null;\n });\n return this.flushPromise;\n }\n\n /** Pushes one batch to Upstash, returning the records to the front of the buffer if the request fails. */\n private async flushBatch() {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n\n try {\n const commands: any[][] = [['LPUSH', this.listName, ...logs.map(log => JSON.stringify(log))]];\n\n // Trim the list if it exceeds maxListLength (must be a separate pipeline command)\n if (this.maxListLength > 0) {\n commands.push(['LTRIM', this.listName, 0, this.maxListLength - 1]);\n }\n\n await this.executeUpstashCommands(commands);\n this.lastFlush = now;\n } catch (error) {\n // On error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n }\n\n /** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */\n private enforceBufferLimit() {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow > 0) {\n this.logBuffer.splice(0, overflow);\n this.droppedRecords += overflow;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function) {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n\n // Flush once a batch is ready, or earlier if the buffer limit is smaller than a batch\n if (this.logBuffer.length >= Math.min(this.batchSize, this.maxBufferSize)) {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }\n\n this.enforceBufferLimit();\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function) {\n clearInterval(this.flushIntervalId);\n\n // Final flush\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.drainBuffer()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n // Wait out any in-flight request, ignoring its failure since its records are back in the buffer,\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n private async drainBuffer(): Promise<void> {\n await this.flushPromise?.catch(() => {});\n while (this.logBuffer.length > 0 || this.flushPromise) {\n await (this.flushPromise ?? this._flush());\n }\n }\n\n private parseLogs(logs: unknown): BaseLogMessage[] {\n if (!Array.isArray(logs)) return [];\n\n return logs.flatMap((log: string) => {\n try {\n const parsedLog = JSON.parse(log);\n return parsedLog !== null && typeof parsedLog === 'object' ? [parsedLog] : [];\n } catch {\n return [];\n }\n });\n }\n\n private matchesLog(\n log: BaseLogMessage,\n {\n fromDate,\n toDate,\n logLevel,\n filters,\n runId,\n }: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n runId?: string;\n },\n ): boolean {\n if (runId !== undefined && log.runId !== runId) return false;\n if (logLevel && log.level !== logLevel) return false;\n const logTime = new Date(log.time).getTime();\n if (fromDate && !(logTime >= fromDate.getTime())) return false;\n if (toDate && !(logTime <= toDate.getTime())) return false;\n\n return !filters || Object.entries(filters).every(([key, value]) => log[key as keyof BaseLogMessage] === value);\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean; // default true\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(params?.page, params?.perPage);\n try {\n const {\n fromDate,\n toDate,\n logLevel,\n filters,\n returnPaginationResults: returnPaginationResultsInput,\n } = params || {};\n\n const returnPaginationResults = returnPaginationResultsInput ?? true;\n const hasFilters = Boolean(fromDate || toDate || logLevel || (filters && Object.keys(filters).length > 0));\n\n if (returnPaginationResults && !hasFilters) {\n const response = await this.executeUpstashCommands([\n ['LLEN', this.listName],\n ['LRANGE', this.listName, start, end - 1],\n ]);\n const total = Number(response?.[0]?.result) || 0;\n const logs = this.parseLogs(response?.[1]?.result);\n\n return {\n logs,\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n }\n\n const response = await this.executeUpstashCommands([['LRANGE', this.listName, 0, -1]]);\n const filteredLogs = this.parseLogs(response?.[0]?.result).filter(log =>\n this.matchesLog(log, { fromDate, toDate, logLevel, filters }),\n );\n\n if (!returnPaginationResults) {\n return {\n logs: filteredLogs,\n total: filteredLogs.length,\n page,\n perPage: filteredLogs.length,\n hasMore: false,\n };\n }\n\n const total = filteredLogs.length;\n\n return {\n logs: filteredLogs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n\n async listLogsByRunId({\n runId,\n fromDate,\n toDate,\n logLevel,\n filters,\n page: pageInput,\n perPage: perPageInput,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(pageInput, perPageInput);\n try {\n const allLogs = await this.listLogs({ fromDate, toDate, logLevel, filters, returnPaginationResults: false });\n const logs = allLogs.logs.filter(log => this.matchesLog(log, { runId }));\n const total = logs.length;\n\n return {\n logs: logs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs by runId from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/upstash/index.ts"],"sourcesContent":["import { LoggerTransport } from '@mastra/core/logger';\nimport type { BaseLogMessage, LogLevel } from '@mastra/core/logger';\nimport { normalizePagination } from '../pagination';\n\n/** LTRIM failed after LPUSH succeeded: the records were written, so they are not put back in the buffer. */\nclass UpstashTrimError extends Error {\n name = 'UpstashTrimError';\n}\n\nexport class UpstashTransport extends LoggerTransport {\n upstashUrl: string;\n upstashToken: string;\n listName: string;\n maxListLength: number;\n maxBufferSize: number;\n /** Number of buffered records discarded because the buffer exceeded `maxBufferSize`. */\n droppedRecords = 0;\n batchSize: number;\n flushInterval: number;\n logBuffer: any[];\n lastFlush: number;\n flushIntervalId: NodeJS.Timeout;\n private flushPromise: Promise<void> | null = null;\n\n constructor(opts: {\n listName?: string;\n maxListLength?: number;\n /** Maximum number of records held in memory while Upstash is unreachable. Must be a positive integer. Defaults to `maxListLength`, or 10000 when `maxListLength` disables trimming. */\n maxBufferSize?: number;\n batchSize?: number;\n upstashUrl: string;\n flushInterval?: number;\n upstashToken: string;\n }) {\n super({ objectMode: true });\n\n if (!opts.upstashUrl || !opts.upstashToken) {\n throw new Error('Upstash URL and token are required');\n }\n\n this.upstashUrl = opts.upstashUrl;\n this.upstashToken = opts.upstashToken;\n this.listName = opts.listName || 'application-logs';\n this.maxListLength = opts.maxListLength || 10000;\n // A non-positive maxListLength disables LTRIM, so it cannot serve as the buffer limit.\n this.maxBufferSize = opts.maxBufferSize ?? (this.maxListLength > 0 ? this.maxListLength : 10000);\n if (!Number.isInteger(this.maxBufferSize) || this.maxBufferSize < 1) {\n throw new Error('maxBufferSize must be a positive integer');\n }\n this.batchSize = opts.batchSize || 100;\n this.flushInterval = opts.flushInterval || 10000;\n\n this.logBuffer = [];\n this.lastFlush = Date.now();\n\n // Start flush interval\n this.flushIntervalId = setInterval(() => {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }, this.flushInterval);\n this.updateIntervalRef();\n }\n\n // The interval only keeps the process alive while records are waiting to be sent, so an idle transport\n // doesn't stop a script from exiting, but buffered records still get delivered (or retried) first.\n // Web-standard runtimes return a numeric timer id without ref()/unref(), so those calls are optional.\n private updateIntervalRef() {\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.flushIntervalId.ref?.();\n } else {\n this.flushIntervalId.unref?.();\n }\n }\n\n private async executeUpstashCommands(commands: any[][]): Promise<any> {\n const response = await fetch(`${this.upstashUrl}/pipeline`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.upstashToken}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(commands),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to execute Upstash command: ${response.statusText}`);\n }\n\n return response.json();\n }\n\n /** Sends the next batch to Upstash. Callers share the in-flight flush instead of starting a new request. */\n _flush(): Promise<void> {\n if (this.flushPromise) {\n return this.flushPromise;\n }\n if (this.logBuffer.length === 0) {\n return Promise.resolve();\n }\n\n this.flushPromise = this.flushBatch().finally(() => {\n this.flushPromise = null;\n this.updateIntervalRef();\n });\n return this.flushPromise;\n }\n\n /**\n * Pushes one batch to Upstash. If the request or the LPUSH command fails, the records return to the\n * front of the buffer. A failed LTRIM is reported without resending, since the records were already written.\n */\n private async flushBatch() {\n const now = Date.now();\n const logs = this.logBuffer.splice(0, this.batchSize);\n const commands: any[][] = [['LPUSH', this.listName, ...logs.map(log => JSON.stringify(log))]];\n\n // Trim the list if it exceeds maxListLength (must be a separate pipeline command)\n if (this.maxListLength > 0) {\n commands.push(['LTRIM', this.listName, 0, this.maxListLength - 1]);\n }\n\n let results: any;\n try {\n results = await this.executeUpstashCommands(commands);\n // Upstash answers HTTP 200 even when a command fails, reporting { error } per command\n const pushError = Array.isArray(results) ? results[0]?.error : undefined;\n if (pushError) {\n throw new Error(`Failed to push logs to Upstash: ${pushError}`);\n }\n } catch (error) {\n // On error, put logs back in the buffer\n this.logBuffer.unshift(...logs);\n this.enforceBufferLimit();\n throw error;\n }\n\n this.lastFlush = now;\n const trimError = Array.isArray(results) ? results[1]?.error : undefined;\n if (trimError) {\n throw new UpstashTrimError(`Failed to trim Upstash log list: ${trimError}`);\n }\n }\n\n /** Drops the oldest buffered records once the buffer exceeds `maxBufferSize`. */\n private enforceBufferLimit() {\n const overflow = this.logBuffer.length - this.maxBufferSize;\n if (overflow > 0) {\n this.logBuffer.splice(0, overflow);\n this.droppedRecords += overflow;\n }\n }\n\n _write(chunk: any, encoding?: string, callback?: (error?: Error | null) => void): boolean {\n if (typeof callback === 'function') {\n this._transform(chunk, encoding || 'utf8', callback);\n return true;\n }\n\n this._transform(chunk, encoding || 'utf8', (error: Error | null) => {\n if (error) console.error('Transform error in write:', error);\n });\n return true;\n }\n\n _transform(chunk: string, _enc: string, cb: Function) {\n try {\n // Parse the log line if it's a string\n const parsed = typeof chunk === 'string' ? JSON.parse(chunk) : chunk;\n\n // Add timestamp if not present, on a copy so caller-owned (possibly frozen) records stay untouched\n const log = parsed.time === undefined || parsed.time === null ? { ...parsed, time: Date.now() } : parsed;\n\n // Add to buffer\n this.logBuffer.push(log);\n this.updateIntervalRef();\n\n // Flush once a batch is ready, or earlier if the buffer limit is smaller than a batch\n if (this.logBuffer.length >= Math.min(this.batchSize, this.maxBufferSize)) {\n this._flush().catch(err => {\n console.error('Error flushing logs to Upstash:', err);\n });\n }\n\n this.enforceBufferLimit();\n\n // Pass through the log\n cb(null, chunk);\n } catch (error) {\n cb(error);\n }\n }\n\n _destroy(err: Error, cb: Function) {\n clearInterval(this.flushIntervalId);\n\n // Final flush\n if (this.logBuffer.length > 0 || this.flushPromise) {\n this.drainBuffer()\n .then(() => cb(err))\n .catch(flushErr => {\n console.error('Error in final flush:', flushErr);\n cb(err || flushErr);\n });\n } else {\n cb(err);\n }\n }\n\n // Wait out any in-flight request, ignoring its failure since its records are back in the buffer,\n // then keep sending until the buffer is empty. A failed request ends the drain instead of retrying forever.\n // A trim failure leaves nothing to resend, so draining continues and the first one is reported at the end.\n private async drainBuffer(): Promise<void> {\n let trimError: UpstashTrimError | undefined;\n await this.flushPromise?.catch(error => {\n if (error instanceof UpstashTrimError) trimError ??= error;\n });\n while (this.logBuffer.length > 0 || this.flushPromise) {\n try {\n await (this.flushPromise ?? this._flush());\n } catch (error) {\n if (!(error instanceof UpstashTrimError)) throw error;\n trimError ??= error;\n }\n }\n if (trimError) throw trimError;\n }\n\n private parseLogs(logs: unknown): BaseLogMessage[] {\n if (!Array.isArray(logs)) return [];\n\n return logs.flatMap((log: string) => {\n try {\n const parsedLog = JSON.parse(log);\n return parsedLog !== null && typeof parsedLog === 'object' ? [parsedLog] : [];\n } catch {\n return [];\n }\n });\n }\n\n private matchesLog(\n log: BaseLogMessage,\n {\n fromDate,\n toDate,\n logLevel,\n filters,\n runId,\n }: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n runId?: string;\n },\n ): boolean {\n if (runId !== undefined && log.runId !== runId) return false;\n if (logLevel && log.level !== logLevel) return false;\n const logTime = new Date(log.time).getTime();\n if (fromDate && !(logTime >= fromDate.getTime())) return false;\n if (toDate && !(logTime <= toDate.getTime())) return false;\n\n return !filters || Object.entries(filters).every(([key, value]) => log[key as keyof BaseLogMessage] === value);\n }\n\n async listLogs(params?: {\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n returnPaginationResults?: boolean; // default true\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(params?.page, params?.perPage);\n try {\n const {\n fromDate,\n toDate,\n logLevel,\n filters,\n returnPaginationResults: returnPaginationResultsInput,\n } = params || {};\n\n const returnPaginationResults = returnPaginationResultsInput ?? true;\n const hasFilters = Boolean(fromDate || toDate || logLevel || (filters && Object.keys(filters).length > 0));\n\n if (returnPaginationResults && !hasFilters) {\n const response = await this.executeUpstashCommands([\n ['LLEN', this.listName],\n ['LRANGE', this.listName, start, end - 1],\n ]);\n const total = Number(response?.[0]?.result) || 0;\n const logs = this.parseLogs(response?.[1]?.result);\n\n return {\n logs,\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n }\n\n const response = await this.executeUpstashCommands([['LRANGE', this.listName, 0, -1]]);\n const filteredLogs = this.parseLogs(response?.[0]?.result).filter(log =>\n this.matchesLog(log, { fromDate, toDate, logLevel, filters }),\n );\n\n if (!returnPaginationResults) {\n return {\n logs: filteredLogs,\n total: filteredLogs.length,\n page,\n perPage: filteredLogs.length,\n hasMore: false,\n };\n }\n\n const total = filteredLogs.length;\n\n return {\n logs: filteredLogs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n\n async listLogsByRunId({\n runId,\n fromDate,\n toDate,\n logLevel,\n filters,\n page: pageInput,\n perPage: perPageInput,\n }: {\n runId: string;\n fromDate?: Date;\n toDate?: Date;\n logLevel?: LogLevel;\n filters?: Record<string, any>;\n page?: number;\n perPage?: number;\n }): Promise<{\n logs: BaseLogMessage[];\n total: number;\n page: number;\n perPage: number;\n hasMore: boolean;\n }> {\n const { page, perPage, start, end } = normalizePagination(pageInput, perPageInput);\n try {\n const allLogs = await this.listLogs({ fromDate, toDate, logLevel, filters, returnPaginationResults: false });\n const logs = allLogs.logs.filter(log => this.matchesLog(log, { runId }));\n const total = logs.length;\n\n return {\n logs: logs.slice(start, end),\n total,\n page,\n perPage,\n hasMore: end < total,\n };\n } catch (error) {\n console.error('Error getting logs by runId from Upstash:', error);\n return {\n logs: [],\n total: 0,\n page,\n perPage,\n hasMore: false,\n };\n }\n }\n}\n"],"mappings":";;;;AAKA,IAAM,mBAAN,cAA+B,MAAM;CACnC,OAAO;AACT;AAEA,IAAa,mBAAb,cAAsC,gBAAgB;CACpD;CACA;CACA;CACA;CACA;;CAEA,iBAAiB;CACjB;CACA;CACA;CACA;CACA;CACA,eAA6C;CAE7C,YAAY,MAST;EACD,MAAM,EAAE,YAAY,KAAK,CAAC;EAE1B,IAAI,CAAC,KAAK,cAAc,CAAC,KAAK,cAC5B,MAAM,IAAI,MAAM,oCAAoC;EAGtD,KAAK,aAAa,KAAK;EACvB,KAAK,eAAe,KAAK;EACzB,KAAK,WAAW,KAAK,YAAY;EACjC,KAAK,gBAAgB,KAAK,iBAAiB;EAE3C,KAAK,gBAAgB,KAAK,kBAAkB,KAAK,gBAAgB,IAAI,KAAK,gBAAgB;EAC1F,IAAI,CAAC,OAAO,UAAU,KAAK,aAAa,KAAK,KAAK,gBAAgB,GAChE,MAAM,IAAI,MAAM,0CAA0C;EAE5D,KAAK,YAAY,KAAK,aAAa;EACnC,KAAK,gBAAgB,KAAK,iBAAiB;EAE3C,KAAK,YAAY,CAAC;EAClB,KAAK,YAAY,KAAK,IAAI;EAG1B,KAAK,kBAAkB,kBAAkB;GACvC,KAAK,OAAO,CAAC,CAAC,OAAM,QAAO;IACzB,QAAQ,MAAM,mCAAmC,GAAG;GACtD,CAAC;EACH,GAAG,KAAK,aAAa;EACrB,KAAK,kBAAkB;CACzB;CAKA,oBAA4B;EAC1B,IAAI,KAAK,UAAU,SAAS,KAAK,KAAK,cACpC,KAAK,gBAAgB,MAAM;OAE3B,KAAK,gBAAgB,QAAQ;CAEjC;CAEA,MAAc,uBAAuB,UAAiC;EACpE,MAAM,WAAW,MAAM,MAAM,GAAG,KAAK,WAAW,YAAY;GAC1D,QAAQ;GACR,SAAS;IACP,eAAe,UAAU,KAAK;IAC9B,gBAAgB;GAClB;GACA,MAAM,KAAK,UAAU,QAAQ;EAC/B,CAAC;EAED,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,sCAAsC,SAAS,YAAY;EAG7E,OAAO,SAAS,KAAK;CACvB;;CAGA,SAAwB;EACtB,IAAI,KAAK,cACP,OAAO,KAAK;EAEd,IAAI,KAAK,UAAU,WAAW,GAC5B,OAAO,QAAQ,QAAQ;EAGzB,KAAK,eAAe,KAAK,WAAW,CAAC,CAAC,cAAc;GAClD,KAAK,eAAe;GACpB,KAAK,kBAAkB;EACzB,CAAC;EACD,OAAO,KAAK;CACd;;;;;CAMA,MAAc,aAAa;EACzB,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,OAAO,KAAK,UAAU,OAAO,GAAG,KAAK,SAAS;EACpD,MAAM,WAAoB,CAAC;GAAC;GAAS,KAAK;GAAU,GAAG,KAAK,KAAI,QAAO,KAAK,UAAU,GAAG,CAAC;EAAC,CAAC;EAG5F,IAAI,KAAK,gBAAgB,GACvB,SAAS,KAAK;GAAC;GAAS,KAAK;GAAU;GAAG,KAAK,gBAAgB;EAAC,CAAC;EAGnE,IAAI;EACJ,IAAI;GACF,UAAU,MAAM,KAAK,uBAAuB,QAAQ;GAEpD,MAAM,YAAY,MAAM,QAAQ,OAAO,IAAI,QAAQ,EAAE,EAAE,QAAQ,KAAA;GAC/D,IAAI,WACF,MAAM,IAAI,MAAM,mCAAmC,WAAW;EAElE,SAAS,OAAO;GAEd,KAAK,UAAU,QAAQ,GAAG,IAAI;GAC9B,KAAK,mBAAmB;GACxB,MAAM;EACR;EAEA,KAAK,YAAY;EACjB,MAAM,YAAY,MAAM,QAAQ,OAAO,IAAI,QAAQ,EAAE,EAAE,QAAQ,KAAA;EAC/D,IAAI,WACF,MAAM,IAAI,iBAAiB,oCAAoC,WAAW;CAE9E;;CAGA,qBAA6B;EAC3B,MAAM,WAAW,KAAK,UAAU,SAAS,KAAK;EAC9C,IAAI,WAAW,GAAG;GAChB,KAAK,UAAU,OAAO,GAAG,QAAQ;GACjC,KAAK,kBAAkB;EACzB;CACF;CAEA,OAAO,OAAY,UAAmB,UAAoD;EACxF,IAAI,OAAO,aAAa,YAAY;GAClC,KAAK,WAAW,OAAO,YAAY,QAAQ,QAAQ;GACnD,OAAO;EACT;EAEA,KAAK,WAAW,OAAO,YAAY,SAAS,UAAwB;GAClE,IAAI,OAAO,QAAQ,MAAM,6BAA6B,KAAK;EAC7D,CAAC;EACD,OAAO;CACT;CAEA,WAAW,OAAe,MAAc,IAAc;EACpD,IAAI;GAEF,MAAM,SAAS,OAAO,UAAU,WAAW,KAAK,MAAM,KAAK,IAAI;GAG/D,MAAM,MAAM,OAAO,SAAS,KAAA,KAAa,OAAO,SAAS,OAAO;IAAE,GAAG;IAAQ,MAAM,KAAK,IAAI;GAAE,IAAI;GAGlG,KAAK,UAAU,KAAK,GAAG;GACvB,KAAK,kBAAkB;GAGvB,IAAI,KAAK,UAAU,UAAU,KAAK,IAAI,KAAK,WAAW,KAAK,aAAa,GACtE,KAAK,OAAO,CAAC,CAAC,OAAM,QAAO;IACzB,QAAQ,MAAM,mCAAmC,GAAG;GACtD,CAAC;GAGH,KAAK,mBAAmB;GAGxB,GAAG,MAAM,KAAK;EAChB,SAAS,OAAO;GACd,GAAG,KAAK;EACV;CACF;CAEA,SAAS,KAAY,IAAc;EACjC,cAAc,KAAK,eAAe;EAGlC,IAAI,KAAK,UAAU,SAAS,KAAK,KAAK,cACpC,KAAK,YAAY,CAAC,CACf,WAAW,GAAG,GAAG,CAAC,CAAC,CACnB,OAAM,aAAY;GACjB,QAAQ,MAAM,yBAAyB,QAAQ;GAC/C,GAAG,OAAO,QAAQ;EACpB,CAAC;OAEH,GAAG,GAAG;CAEV;CAKA,MAAc,cAA6B;EACzC,IAAI;EACJ,MAAM,KAAK,cAAc,OAAM,UAAS;GACtC,IAAI,iBAAiB,kBAAkB,cAAc;EACvD,CAAC;EACD,OAAO,KAAK,UAAU,SAAS,KAAK,KAAK,cACvC,IAAI;GACF,OAAO,KAAK,gBAAgB,KAAK,OAAO;EAC1C,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,mBAAmB,MAAM;GAChD,cAAc;EAChB;EAEF,IAAI,WAAW,MAAM;CACvB;CAEA,UAAkB,MAAiC;EACjD,IAAI,CAAC,MAAM,QAAQ,IAAI,GAAG,OAAO,CAAC;EAElC,OAAO,KAAK,SAAS,QAAgB;GACnC,IAAI;IACF,MAAM,YAAY,KAAK,MAAM,GAAG;IAChC,OAAO,cAAc,QAAQ,OAAO,cAAc,WAAW,CAAC,SAAS,IAAI,CAAC;GAC9E,QAAQ;IACN,OAAO,CAAC;GACV;EACF,CAAC;CACH;CAEA,WACE,KACA,EACE,UACA,QACA,UACA,SACA,SAQO;EACT,IAAI,UAAU,KAAA,KAAa,IAAI,UAAU,OAAO,OAAO;EACvD,IAAI,YAAY,IAAI,UAAU,UAAU,OAAO;EAC/C,MAAM,UAAU,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ;EAC3C,IAAI,YAAY,EAAE,WAAW,SAAS,QAAQ,IAAI,OAAO;EACzD,IAAI,UAAU,EAAE,WAAW,OAAO,QAAQ,IAAI,OAAO;EAErD,OAAO,CAAC,WAAW,OAAO,QAAQ,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,WAAW,IAAI,SAAiC,KAAK;CAC/G;CAEA,MAAM,SAAS,QAcZ;EACD,MAAM,EAAE,MAAM,SAAS,OAAO,QAAQ,oBAAoB,QAAQ,MAAM,QAAQ,OAAO;EACvF,IAAI;GACF,MAAM,EACJ,UACA,QACA,UACA,SACA,yBAAyB,iCACvB,UAAU,CAAC;GAEf,MAAM,0BAA0B,gCAAgC;GAChE,MAAM,aAAa,QAAQ,YAAY,UAAU,YAAa,WAAW,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,CAAE;GAEzG,IAAI,2BAA2B,CAAC,YAAY;IAC1C,MAAM,WAAW,MAAM,KAAK,uBAAuB,CACjD,CAAC,QAAQ,KAAK,QAAQ,GACtB;KAAC;KAAU,KAAK;KAAU;KAAO,MAAM;IAAC,CAC1C,CAAC;IACD,MAAM,QAAQ,OAAO,WAAW,EAAE,EAAE,MAAM,KAAK;IAG/C,OAAO;KACL,MAHW,KAAK,UAAU,WAAW,EAAE,EAAE,MAGtC;KACH;KACA;KACA;KACA,SAAS,MAAM;IACjB;GACF;GAEA,MAAM,WAAW,MAAM,KAAK,uBAAuB,CAAC;IAAC;IAAU,KAAK;IAAU;IAAG;GAAE,CAAC,CAAC;GACrF,MAAM,eAAe,KAAK,UAAU,WAAW,EAAE,EAAE,MAAM,CAAC,CAAC,QAAO,QAChE,KAAK,WAAW,KAAK;IAAE;IAAU;IAAQ;IAAU;GAAQ,CAAC,CAC9D;GAEA,IAAI,CAAC,yBACH,OAAO;IACL,MAAM;IACN,OAAO,aAAa;IACpB;IACA,SAAS,aAAa;IACtB,SAAS;GACX;GAGF,MAAM,QAAQ,aAAa;GAE3B,OAAO;IACL,MAAM,aAAa,MAAM,OAAO,GAAG;IACnC;IACA;IACA;IACA,SAAS,MAAM;GACjB;EACF,SAAS,OAAO;GACd,QAAQ,MAAM,oCAAoC,KAAK;GACvD,OAAO;IACL,MAAM,CAAC;IACP,OAAO;IACP;IACA;IACA,SAAS;GACX;EACF;CACF;CAEA,MAAM,gBAAgB,EACpB,OACA,UACA,QACA,UACA,SACA,MAAM,WACN,SAAS,gBAeR;EACD,MAAM,EAAE,MAAM,SAAS,OAAO,QAAQ,oBAAoB,WAAW,YAAY;EACjF,IAAI;GAEF,MAAM,QAAO,MADS,KAAK,SAAS;IAAE;IAAU;IAAQ;IAAU;IAAS,yBAAyB;GAAM,CAAC,EAAA,CACtF,KAAK,QAAO,QAAO,KAAK,WAAW,KAAK,EAAE,MAAM,CAAC,CAAC;GACvE,MAAM,QAAQ,KAAK;GAEnB,OAAO;IACL,MAAM,KAAK,MAAM,OAAO,GAAG;IAC3B;IACA;IACA;IACA,SAAS,MAAM;GACjB;EACF,SAAS,OAAO;GACd,QAAQ,MAAM,6CAA6C,KAAK;GAChE,OAAO;IACL,MAAM,CAAC;IACP,OAAO;IACP;IACA;IACA,SAAS;GACX;EACF;CACF;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/loggers",
3
- "version": "1.3.9-alpha.0",
3
+ "version": "1.3.9-alpha.2",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "files": [
@@ -71,14 +71,13 @@
71
71
  "@types/node": "22.20.1",
72
72
  "@vitest/coverage-v8": "4.1.11",
73
73
  "@vitest/ui": "4.1.11",
74
- "eslint": "^10.7.0",
75
74
  "tsdown": "0.22.9",
76
75
  "tsx": "^4.23.1",
77
76
  "typescript": "^7.0.2",
78
77
  "vitest": "4.1.11",
79
- "@internal/types-builder": "0.0.119",
80
78
  "@internal/lint": "0.0.144",
81
- "@mastra/core": "1.78.1-alpha.0"
79
+ "@internal/types-builder": "0.0.119",
80
+ "@mastra/core": "1.79.0-alpha.6"
82
81
  },
83
82
  "peerDependencies": {
84
83
  "@mastra/core": ">=1.0.0-0 <2.0.0-0"
@@ -90,7 +89,7 @@
90
89
  "build:lib": "tsdown --silent --config tsdown.config.ts",
91
90
  "build:watch": "tsdown --watch --silent --config tsdown.config.ts",
92
91
  "test": "vitest run",
93
- "lint": "oxlint . && eslint .",
94
- "lint:fix": "oxlint --fix . && eslint --fix ."
92
+ "lint": "oxlint --type-aware .",
93
+ "lint:fix": "oxlint --type-aware --fix ."
95
94
  }
96
95
  }