@mastra/duckdb 1.16.0 → 1.17.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +1 -1
  2. package/dist/docs/SKILL.md +2 -2
  3. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  4. package/dist/docs/references/integrations-databases-duckdb.md +2 -2
  5. package/dist/{features-DH60yLCA.cjs → features-CBdrOdEO.cjs} +98 -9
  6. package/dist/features-CBdrOdEO.cjs.map +1 -0
  7. package/dist/{features-B8gl38Ue.js → features-D9RrctvA.js} +71 -6
  8. package/dist/features-D9RrctvA.js.map +1 -0
  9. package/dist/index.cjs +25 -8
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.js +25 -8
  12. package/dist/index.js.map +1 -1
  13. package/dist/{observability-BEdkwaeY.cjs → observability-B_ARB4Uv.cjs} +85 -24
  14. package/dist/observability-B_ARB4Uv.cjs.map +1 -0
  15. package/dist/{observability-B-Kt8vrw.js → observability-CN4Rwgv9.js} +85 -24
  16. package/dist/observability-CN4Rwgv9.js.map +1 -0
  17. package/dist/storage/db/index.d.ts +5 -0
  18. package/dist/storage/db/index.d.ts.map +1 -1
  19. package/dist/storage/domains/observability/index.d.ts +1 -1
  20. package/dist/storage/domains/observability/index.d.ts.map +1 -1
  21. package/dist/storage/domains/observability/preview-compat.d.ts +4 -0
  22. package/dist/storage/domains/observability/preview-compat.d.ts.map +1 -1
  23. package/dist/storage/domains/observability/span-query.d.ts +4 -0
  24. package/dist/storage/domains/observability/span-query.d.ts.map +1 -1
  25. package/dist/storage/domains/observability/thread-query.d.ts +1 -0
  26. package/dist/storage/domains/observability/thread-query.d.ts.map +1 -1
  27. package/dist/storage/domains/observability/trace-aggregate.d.ts.map +1 -1
  28. package/dist/storage/domains/observability/trace-query.d.ts.map +1 -1
  29. package/dist/storage/features.d.ts +9 -3
  30. package/dist/storage/features.d.ts.map +1 -1
  31. package/dist/storage/index.d.ts +3 -0
  32. package/dist/storage/index.d.ts.map +1 -1
  33. package/package.json +2 -2
  34. package/dist/features-B8gl38Ue.js.map +0 -1
  35. package/dist/features-DH60yLCA.cjs.map +0 -1
  36. package/dist/observability-B-Kt8vrw.js.map +0 -1
  37. package/dist/observability-BEdkwaeY.cjs.map +0 -1
  38. /package/dist/docs/references/{reference-vectors-duckdb.md → integrations-vectors-duckdb.md} +0 -0
package/README.md CHANGED
@@ -32,7 +32,7 @@ const mastra = new Mastra({
32
32
  ## Documentation
33
33
 
34
34
  - [DuckDB integration guide](https://mastra.ai/integrations/databases/duckdb)
35
- - [DuckDB vector reference](https://mastra.ai/reference/vectors/duckdb)
35
+ - [DuckDB vector reference](https://mastra.ai/integrations/vectors/duckdb)
36
36
 
37
37
  ## Changelog
38
38
 
@@ -3,7 +3,7 @@ name: mastra-duckdb
3
3
  description: Documentation for @mastra/duckdb. Use when working with @mastra/duckdb APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/duckdb"
6
- version: "1.16.0"
6
+ version: "1.17.0-alpha.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -17,11 +17,11 @@ Read the individual reference documents for detailed explanations and code examp
17
17
  ### Integrations
18
18
 
19
19
  - [DuckDB](references/integrations-databases-duckdb.md) - Store Mastra observability data and metrics in embedded DuckDB for local analytics, either standalone or as a specialized composite storage domain.
20
+ - [DuckDB](references/integrations-vectors-duckdb.md) - Run embedded vector search in DuckDB from Mastra using the VSS extension and HNSW indexes, with no external database server to manage.
20
21
 
21
22
  ### Reference
22
23
 
23
24
  - [Reference: Storage retention (prune)](references/reference-storage-retention.md) - prune() deletes rows. It caps growth and is safe to run against large tables (batched, bounded, resumable, cancellable).
24
- - [Reference: DuckDB vector store](references/reference-vectors-duckdb.md) - The DuckDB storage implementation provides an embedded high-performance vector search solution using DuckDB, an in-process analytical database.
25
25
 
26
26
 
27
27
  Read [assets/SOURCE_MAP.json](assets/SOURCE_MAP.json) for source code references.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.16.0",
2
+ "version": "1.17.0-alpha.1",
3
3
  "package": "@mastra/duckdb",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -6,7 +6,7 @@
6
6
 
7
7
  [DuckDB](https://duckdb.org/) is an embedded, in-process analytical database. The `@mastra/duckdb` package provides an OLAP-backed observability store for local development, suitable for traces, logs, metrics, scores, and feedback without running an external service.
8
8
 
9
- For vector search, see the [DuckDB vector store reference](https://mastra.ai/reference/vectors/duckdb), which is a separate API in the same package.
9
+ For vector search, see the [DuckDB vector store reference](https://mastra.ai/integrations/vectors/duckdb), which is a separate API in the same package.
10
10
 
11
11
  ## When to use DuckDB
12
12
 
@@ -154,5 +154,5 @@ DuckDB supports the `event-sourced` strategy used by `MastraStorageExporter`, wh
154
154
  - [Storage overview](https://mastra.ai/reference/storage/overview)
155
155
  - [Composite storage](https://mastra.ai/reference/storage/composite)
156
156
  - [ClickHouse storage](https://mastra.ai/integrations/databases/clickhouse): Production observability backend
157
- - [DuckDB vector store](https://mastra.ai/reference/vectors/duckdb): Vector search using the same package
157
+ - [DuckDB vector store](https://mastra.ai/integrations/vectors/duckdb): Vector search using the same package
158
158
  - [Observability overview](https://mastra.ai/docs/observability/overview)
@@ -66,6 +66,10 @@ var DuckDBConnection = class extends _mastra_core_base.MastraBase {
66
66
  instance = null;
67
67
  initialized = false;
68
68
  initPromise = null;
69
+ closed = false;
70
+ activeConnections = 0;
71
+ drainPromise = null;
72
+ finishDrain = null;
69
73
  path;
70
74
  instanceOptions;
71
75
  constructor(config = {}) {
@@ -80,6 +84,7 @@ var DuckDBConnection = class extends _mastra_core_base.MastraBase {
80
84
  };
81
85
  }
82
86
  async initialize() {
87
+ if (this.closed) throw new Error("DuckDB connection is closed");
83
88
  if (this.initialized && this.instance) return;
84
89
  if (this.initPromise) {
85
90
  await this.initPromise;
@@ -103,8 +108,46 @@ var DuckDBConnection = class extends _mastra_core_base.MastraBase {
103
108
  /** Create a new connection to the DuckDB instance, initializing if needed. */
104
109
  async getConnection() {
105
110
  await this.initialize();
111
+ if (this.closed) throw new Error("DuckDB connection is closed");
106
112
  if (!this.instance) throw new Error("DuckDB instance not initialized");
107
- return this.instance.connect();
113
+ this.activeConnections++;
114
+ try {
115
+ const connection = await this.instance.connect();
116
+ let released = false;
117
+ const release = () => {
118
+ if (!released) {
119
+ released = true;
120
+ this.releaseConnection();
121
+ }
122
+ };
123
+ return new Proxy(connection, { get(target, key) {
124
+ const value = Reflect.get(target, key, target);
125
+ if (typeof value !== "function") return value;
126
+ if (key === "closeSync" || key === "disconnectSync" || key === "close" || key === "disconnect") return (...args) => {
127
+ try {
128
+ const result = value.apply(target, args);
129
+ if (result instanceof Promise) return result.finally(release);
130
+ release();
131
+ return result;
132
+ } catch (error) {
133
+ release();
134
+ throw error;
135
+ }
136
+ };
137
+ return value.bind(target);
138
+ } });
139
+ } catch (error) {
140
+ this.releaseConnection();
141
+ throw error;
142
+ }
143
+ }
144
+ releaseConnection() {
145
+ this.activeConnections--;
146
+ if (this.activeConnections === 0) {
147
+ this.finishDrain?.();
148
+ this.finishDrain = null;
149
+ this.drainPromise = null;
150
+ }
108
151
  }
109
152
  closeConnection(connection) {
110
153
  const conn = connection;
@@ -265,6 +308,12 @@ var DuckDBConnection = class extends _mastra_core_base.MastraBase {
265
308
  }
266
309
  /** Release the DuckDB instance, allowing garbage collection. */
267
310
  async close() {
311
+ this.closed = true;
312
+ await this.initPromise?.catch(() => void 0);
313
+ if (this.activeConnections > 0) {
314
+ this.drainPromise ??= new Promise((resolve) => this.finishDrain = resolve);
315
+ await this.drainPromise;
316
+ }
268
317
  if (this.instance) {
269
318
  try {
270
319
  const instance = this.instance;
@@ -279,13 +328,29 @@ var DuckDBConnection = class extends _mastra_core_base.MastraBase {
279
328
  };
280
329
  // src/storage/features.ts
281
330
  const threadQueryFeatures = typeof _mastra_core_storage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [];
282
- const spanQueryFeatures = typeof _mastra_core_storage.planSpanQuery === "function" ? ["span-query", "span-query-context-fields"] : [];
283
- const spanAggregateFeatures = typeof _mastra_core_storage.planSpanAggregate === "function" ? ["span-aggregate"] : [];
331
+ const spanQueryFeatures = typeof _mastra_core_storage.planSpanQuery === "function" ? [
332
+ "span-query",
333
+ "span-query-context-fields",
334
+ ..."model" in (_mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY ?? {}) ? ["span-query-model-fields"] : []
335
+ ] : [];
336
+ const spanAggregateFeatures = typeof _mastra_core_storage.planSpanAggregate === "function" ? ["span-aggregate", ..."model" in (_mastra_core_storage.SPAN_AGGREGATE_DIMENSION_REGISTRY ?? {}) ? ["span-aggregate-model-dimensions"] : []] : [];
284
337
  /** Core versions without the field reject it in trace query results, so it is only returned when core knows it. */
285
338
  const timeToFirstChunkSupported = "timeToFirstChunkMs" in _mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY.trace;
286
339
  /** Core before the sort rejects `orderBy` on the field, so the sort is only declared when core plans it. */
287
340
  const timeToFirstChunkSortSupported = _mastra_core_storage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstChunkMs") === true;
288
- const timeToFirstChunkFeatures = [...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [], ...timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : []];
341
+ /** Same rule for time to first token: returned and sorted only when core knows the field. */
342
+ const timeToFirstTokenSupported = "timeToFirstTokenMs" in _mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY.trace;
343
+ const timeToFirstTokenSortSupported = _mastra_core_storage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstTokenMs") === true;
344
+ /** Core before the outcome fields rejects their predicates and strict rows, so they are only returned and declared when core knows them. */
345
+ const spanQueryOutcomeFieldsSupported = "outcome" in (_mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY ?? {});
346
+ const traceQueryOutcomeFieldsSupported = "outcome" in (_mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
347
+ const outcomeFeatures = [...spanQueryOutcomeFieldsSupported ? ["span-query-outcome-fields"] : [], ...traceQueryOutcomeFieldsSupported ? ["trace-query-outcome-fields"] : []];
348
+ const timingFeatures = [
349
+ ...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [],
350
+ ...timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : [],
351
+ ...timeToFirstTokenSupported ? ["trace-query-time-to-first-token"] : [],
352
+ ...timeToFirstTokenSortSupported ? ["trace-query-time-to-first-token-sort"] : []
353
+ ];
289
354
  Object.defineProperty(exports, "DuckDBConnection", {
290
355
  enumerable: true,
291
356
  get: function() {
@@ -310,6 +375,12 @@ Object.defineProperty(exports, "bindParam", {
310
375
  return bindParam;
311
376
  }
312
377
  });
378
+ Object.defineProperty(exports, "outcomeFeatures", {
379
+ enumerable: true,
380
+ get: function() {
381
+ return outcomeFeatures;
382
+ }
383
+ });
313
384
  Object.defineProperty(exports, "spanAggregateFeatures", {
314
385
  enumerable: true,
315
386
  get: function() {
@@ -322,16 +393,16 @@ Object.defineProperty(exports, "spanQueryFeatures", {
322
393
  return spanQueryFeatures;
323
394
  }
324
395
  });
325
- Object.defineProperty(exports, "threadQueryFeatures", {
396
+ Object.defineProperty(exports, "spanQueryOutcomeFieldsSupported", {
326
397
  enumerable: true,
327
398
  get: function() {
328
- return threadQueryFeatures;
399
+ return spanQueryOutcomeFieldsSupported;
329
400
  }
330
401
  });
331
- Object.defineProperty(exports, "timeToFirstChunkFeatures", {
402
+ Object.defineProperty(exports, "threadQueryFeatures", {
332
403
  enumerable: true,
333
404
  get: function() {
334
- return timeToFirstChunkFeatures;
405
+ return threadQueryFeatures;
335
406
  }
336
407
  });
337
408
  Object.defineProperty(exports, "timeToFirstChunkSupported", {
@@ -340,5 +411,23 @@ Object.defineProperty(exports, "timeToFirstChunkSupported", {
340
411
  return timeToFirstChunkSupported;
341
412
  }
342
413
  });
414
+ Object.defineProperty(exports, "timeToFirstTokenSupported", {
415
+ enumerable: true,
416
+ get: function() {
417
+ return timeToFirstTokenSupported;
418
+ }
419
+ });
420
+ Object.defineProperty(exports, "timingFeatures", {
421
+ enumerable: true,
422
+ get: function() {
423
+ return timingFeatures;
424
+ }
425
+ });
426
+ Object.defineProperty(exports, "traceQueryOutcomeFieldsSupported", {
427
+ enumerable: true,
428
+ get: function() {
429
+ return traceQueryOutcomeFieldsSupported;
430
+ }
431
+ });
343
432
 
344
- //# sourceMappingURL=features-DH60yLCA.cjs.map
433
+ //# sourceMappingURL=features-CBdrOdEO.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features-CBdrOdEO.cjs","names":["DuckDBTimestampValue","DuckDBTimestampTZValue","MastraBase","DuckDBInstance","coreStorage"],"sources":["../src/storage/db/index.ts","../src/storage/features.ts"],"sourcesContent":["import { DuckDBInstance, DuckDBTimestampValue, DuckDBTimestampTZValue } from '@duckdb/node-api';\nimport type { DuckDBPreparedStatement } from '@duckdb/node-api';\nimport { MastraBase } from '@mastra/core/base';\n\n/**\n * Bind a single parameter to a prepared statement using explicit typed methods.\n * This avoids the \"Cannot create values of type ANY\" error that occurs when\n * DuckDB cannot infer parameter types from SQL context (e.g. json_extract_string).\n */\nexport function bindParam(stmt: DuckDBPreparedStatement, index: number, value: unknown): void {\n if (value === null || value === undefined) {\n stmt.bindNull(index);\n } else if (typeof value === 'string') {\n stmt.bindVarchar(index, value);\n } else if (typeof value === 'number') {\n if (Number.isInteger(value) && value >= -2147483648 && value <= 2147483647) {\n stmt.bindInteger(index, value);\n } else {\n stmt.bindDouble(index, value);\n }\n } else if (typeof value === 'boolean') {\n stmt.bindBoolean(index, value);\n } else if (typeof value === 'bigint') {\n stmt.bindBigInt(index, value);\n } else if (value instanceof Date) {\n stmt.bindTimestamp(index, new DuckDBTimestampValue(BigInt(value.getTime()) * 1000n));\n } else if (value instanceof DuckDBTimestampValue) {\n stmt.bindTimestamp(index, value);\n } else if (value instanceof DuckDBTimestampTZValue) {\n stmt.bindTimestampTZ(index, value);\n } else {\n // Fallback: serialize to JSON string\n stmt.bindVarchar(index, JSON.stringify(value));\n }\n}\n\n/** Convert DuckDB-specific return types to plain JS types */\nfunction toJsValue(val: unknown): unknown {\n if (val === null || val === undefined) return val;\n // DuckDBTimestampValue → Date (micros since epoch)\n if (val instanceof DuckDBTimestampValue) {\n return new Date(Number(val.micros / 1000n));\n }\n // BigInt → Number (safe for values we care about)\n if (typeof val === 'bigint') {\n return Number(val);\n }\n return val;\n}\n\n/** A query interrupted at its caller-supplied deadline. */\nexport class DuckDBQueryTimeoutError extends Error {\n constructor() {\n super('DuckDB query exceeded its deadline');\n this.name = 'DuckDBQueryTimeoutError';\n }\n}\n\n/** Configuration for the DuckDB database connection. */\nexport interface DuckDBStorageConfig {\n /** Path to the DuckDB file. Defaults to 'mastra.duckdb'. Use ':memory:' for ephemeral. */\n path?: string;\n /**\n * Maximum memory DuckDB may use (e.g. '2GB', '512MB').\n * @default '2GB'\n * DuckDB's own default is 80% of system RAM, which is far too aggressive for\n * a store embedded in an application server — a single query against a large\n * database can balloon the process by several GB and push the host into\n * swap. Larger-than-memory operations spill to disk for file-backed\n * databases. Raise this for dedicated analytical workloads.\n */\n memoryLimit?: string;\n /**\n * Number of threads DuckDB may use. Defaults to DuckDB's default (one per\n * CPU core). Lower this to keep queries from monopolizing all cores of a\n * shared application server.\n */\n threads?: number;\n}\n\nconst DEFAULT_MEMORY_LIMIT = '2GB';\n\n/**\n * Shared DuckDB connection management for Mastra storage.\n * Defaults to a local file (`mastra.duckdb`) when no path is provided.\n * Pass `path: ':memory:'` for an ephemeral in-memory database.\n */\nexport class DuckDBConnection extends MastraBase {\n private instance: DuckDBInstance | null = null;\n private initialized = false;\n private initPromise: Promise<void> | null = null;\n private closed = false;\n private activeConnections = 0;\n private drainPromise: Promise<void> | null = null;\n private finishDrain: (() => void) | null = null;\n private path: string;\n private instanceOptions: Record<string, string>;\n\n constructor(config: DuckDBStorageConfig = {}) {\n super({ component: 'STORAGE', name: 'DUCKDB' });\n this.path = config.path ?? 'mastra.duckdb';\n this.instanceOptions = {\n max_memory: config.memoryLimit ?? DEFAULT_MEMORY_LIMIT,\n ...(config.threads !== undefined ? { threads: String(config.threads) } : {}),\n };\n }\n\n private async initialize(): Promise<void> {\n if (this.closed) throw new Error('DuckDB connection is closed');\n if (this.initialized && this.instance) return;\n\n if (this.initPromise) {\n await this.initPromise;\n if (this.instance) return;\n this.initPromise = null;\n this.initialized = false;\n }\n\n this.initPromise = (async () => {\n try {\n this.instance = await DuckDBInstance.create(this.path, this.instanceOptions);\n this.initialized = true;\n } catch (error) {\n this.instance = null;\n this.initialized = false;\n this.initPromise = null;\n throw error;\n }\n })();\n\n return this.initPromise;\n }\n\n /** Create a new connection to the DuckDB instance, initializing if needed. */\n async getConnection() {\n await this.initialize();\n if (this.closed) throw new Error('DuckDB connection is closed');\n if (!this.instance) {\n throw new Error('DuckDB instance not initialized');\n }\n this.activeConnections++;\n try {\n const connection = await this.instance.connect();\n let released = false;\n const release = () => {\n if (!released) {\n released = true;\n this.releaseConnection();\n }\n };\n return new Proxy(connection, {\n get(target, key) {\n const value = Reflect.get(target, key, target);\n if (typeof value !== 'function') return value;\n if (key === 'closeSync' || key === 'disconnectSync' || key === 'close' || key === 'disconnect') {\n return (...args: unknown[]) => {\n try {\n const result = value.apply(target, args);\n if (result instanceof Promise) return result.finally(release);\n release();\n return result;\n } catch (error) {\n release();\n throw error;\n }\n };\n }\n return value.bind(target);\n },\n });\n } catch (error) {\n this.releaseConnection();\n throw error;\n }\n }\n\n private releaseConnection(): void {\n this.activeConnections--;\n if (this.activeConnections === 0) {\n this.finishDrain?.();\n this.finishDrain = null;\n this.drainPromise = null;\n }\n }\n\n private closeConnection(connection: unknown): void {\n const conn = connection as {\n closeSync?: () => void;\n disconnectSync?: () => void;\n close?: () => void;\n disconnect?: () => void;\n };\n try {\n if (typeof conn?.closeSync === 'function') {\n conn.closeSync();\n return;\n }\n if (typeof conn?.disconnectSync === 'function') {\n conn.disconnectSync();\n return;\n }\n if (typeof conn?.close === 'function') {\n conn.close();\n return;\n }\n if (typeof conn?.disconnect === 'function') {\n conn.disconnect();\n }\n } catch {\n // Ignore close failures to avoid masking query/execute errors.\n }\n }\n\n /**\n * Execute a SQL query and return results as objects.\n */\n async query<T = Record<string, unknown>>(sql: string, params: unknown[] = [], timeoutMs?: number): Promise<T[]> {\n const connection = await this.getConnection();\n let timedOut = false;\n const timer =\n timeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n timedOut = true;\n connection.interrupt();\n }, timeoutMs);\n try {\n if (params.length === 0) {\n const result = await connection.run(sql);\n const rows = await result.getRows();\n const columns = result.columnNames();\n return rows.map(row => {\n const obj: Record<string, unknown> = {};\n columns.forEach((col, i) => {\n obj[col] = toJsValue(row[i]);\n });\n return obj as T;\n });\n }\n\n let paramIndex = 0;\n const preparedSql = sql.replace(/\\?/g, () => `$${++paramIndex}`);\n const stmt = await connection.prepare(preparedSql);\n for (let i = 0; i < params.length; i++) {\n bindParam(stmt, i + 1, params[i]);\n }\n const result = await stmt.run();\n const rows = await result.getRows();\n const columns = result.columnNames();\n return rows.map(row => {\n const obj: Record<string, unknown> = {};\n columns.forEach((col, i) => {\n obj[col] = toJsValue(row[i]);\n });\n return obj as T;\n });\n } catch (error) {\n if (timedOut) throw new DuckDBQueryTimeoutError();\n throw error;\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n this.closeConnection(connection);\n }\n }\n\n /**\n * Execute a SQL statement without returning results.\n */\n async execute(sql: string, params: unknown[] = []): Promise<void> {\n const connection = await this.getConnection();\n try {\n if (params.length === 0) {\n await connection.run(sql);\n return;\n }\n let paramIndex = 0;\n const preparedSql = sql.replace(/\\?/g, () => `$${++paramIndex}`);\n const stmt = await connection.prepare(preparedSql);\n for (let i = 0; i < params.length; i++) {\n bindParam(stmt, i + 1, params[i]);\n }\n await stmt.run();\n } finally {\n this.closeConnection(connection);\n }\n }\n\n /** Delete one bounded retention batch and return the number of rows removed. */\n async pruneBatch({\n tableName,\n column,\n cutoff,\n limit,\n }: {\n tableName: string;\n column: string;\n cutoff: Date;\n limit: number;\n }): Promise<number> {\n const identifier = /^[A-Za-z_][A-Za-z0-9_]*$/;\n if (!identifier.test(tableName) || !identifier.test(column)) {\n throw new Error(`Invalid retention identifier: ${tableName}.${column}`);\n }\n\n const rows = await this.query(\n `DELETE FROM ${tableName}\n WHERE rowid IN (\n SELECT rowid FROM ${tableName}\n WHERE ${column} < ?\n ORDER BY ${column}\n LIMIT ?\n )\n RETURNING 1 AS deleted`,\n [cutoff, limit],\n );\n return rows.length;\n }\n\n /** Execute parameterized statements atomically using a single DuckDB connection. */\n async executeTransaction(statements: readonly { sql: string; params?: readonly unknown[] }[]): Promise<void> {\n if (statements.length === 0) return;\n\n const connection = await this.getConnection();\n try {\n await connection.run('BEGIN TRANSACTION');\n for (const statement of statements) {\n const params = statement.params ?? [];\n if (params.length === 0) {\n await connection.run(statement.sql);\n continue;\n }\n let paramIndex = 0;\n const preparedSql = statement.sql.replace(/\\?/g, () => `$${++paramIndex}`);\n const prepared = await connection.prepare(preparedSql);\n for (let i = 0; i < params.length; i++) {\n bindParam(prepared, i + 1, params[i]);\n }\n await prepared.run();\n }\n await connection.run('COMMIT');\n } catch (error) {\n await connection.run('ROLLBACK').catch(() => undefined);\n throw error;\n } finally {\n this.closeConnection(connection);\n }\n }\n\n /**\n * Execute multiple SQL statements in order using a single DuckDB connection.\n *\n * This is intended for schema setup/migrations where statements have no\n * parameters and must remain ordered, but opening a connection per statement\n * would dominate initialization cost. Blank statements are skipped. Like\n * calling execute() repeatedly, this does not wrap statements in a transaction,\n * so prior statements can remain applied if a later statement fails.\n */\n async executeBatch(sqlStatements: readonly string[]): Promise<void> {\n const statements = sqlStatements.map(statement => statement.trim()).filter(Boolean);\n if (statements.length === 0) return;\n\n const connection = await this.getConnection();\n try {\n const sql =\n statements.map((statement, i) => `-- executeBatch statement ${i + 1}\\n${statement}`).join('\\n;\\n') + '\\n;';\n await connection.run(sql);\n } finally {\n this.closeConnection(connection);\n }\n }\n\n /**\n * Escape a value for safe inline SQL use.\n * DuckDB prepared statements can't handle NULL for parameters typed as ANY,\n * so for complex INSERT/UPDATE operations we inline values safely.\n */\n static sqlValue(value: unknown): string {\n if (value === null || value === undefined) return 'NULL';\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) return 'NULL';\n return String(value);\n }\n if (typeof value === 'boolean') return value ? 'TRUE' : 'FALSE';\n if (value instanceof Date) return `'${value.toISOString()}'::TIMESTAMP`;\n if (typeof value === 'string') return `'${value.replace(/'/g, \"''\")}'`;\n // Objects/arrays → JSON string\n return `'${JSON.stringify(value).replace(/'/g, \"''\")}'`;\n }\n\n /** Release the DuckDB instance, allowing garbage collection. */\n async close(): Promise<void> {\n this.closed = true;\n await this.initPromise?.catch(() => undefined);\n if (this.activeConnections > 0) {\n this.drainPromise ??= new Promise<void>(resolve => (this.finishDrain = resolve));\n await this.drainPromise;\n }\n if (this.instance) {\n try {\n const instance = this.instance as unknown as { closeSync?: () => void; close?: () => void };\n if (typeof instance.closeSync === 'function') {\n instance.closeSync();\n } else if (typeof instance.close === 'function') {\n instance.close();\n }\n } catch {\n // Ignore close failures to allow cleanup of references.\n }\n this.instance = null;\n this.initialized = false;\n this.initPromise = null;\n }\n }\n}\n","import * as coreStorage from '@mastra/core/storage';\n\nexport const threadQueryFeatures =\n typeof coreStorage.planThreadRowsQuery === 'function' ? (['thread-query-rows'] as const) : ([] as const);\n\nexport const spanQueryFeatures =\n typeof coreStorage.planSpanQuery === 'function'\n ? ([\n 'span-query',\n 'span-query-context-fields',\n // Core before the fields rejects `model`/`provider` predicates, so only declare them when core plans them.\n ...('model' in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {}) ? (['span-query-model-fields'] as const) : []),\n ] as const)\n : ([] as const);\n\nexport const spanAggregateFeatures =\n typeof coreStorage.planSpanAggregate === 'function'\n ? ([\n 'span-aggregate',\n // Core before the dimensions rejects `model`/`provider` in `groupBy`, so only declare them when core plans them.\n ...('model' in (coreStorage.SPAN_AGGREGATE_DIMENSION_REGISTRY ?? {})\n ? (['span-aggregate-model-dimensions'] as const)\n : []),\n ] as const)\n : ([] as const);\n\n/** Core versions without the field reject it in trace query results, so it is only returned when core knows it. */\nexport const timeToFirstChunkSupported = 'timeToFirstChunkMs' in coreStorage.TRACE_QUERY_FIELD_REGISTRY.trace;\n\n/** Core before the sort rejects `orderBy` on the field, so the sort is only declared when core plans it. */\nconst timeToFirstChunkSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.includes('timeToFirstChunkMs') === true;\n\n/** Same rule for time to first token: returned and sorted only when core knows the field. */\nexport const timeToFirstTokenSupported = 'timeToFirstTokenMs' in coreStorage.TRACE_QUERY_FIELD_REGISTRY.trace;\nconst timeToFirstTokenSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.includes('timeToFirstTokenMs') === true;\n\n/** Core before the outcome fields rejects their predicates and strict rows, so they are only returned and declared when core knows them. */\nexport const spanQueryOutcomeFieldsSupported = 'outcome' in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {});\nexport const traceQueryOutcomeFieldsSupported = 'outcome' in (coreStorage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});\n\nexport const outcomeFeatures: readonly ('span-query-outcome-fields' | 'trace-query-outcome-fields')[] = [\n ...(spanQueryOutcomeFieldsSupported ? (['span-query-outcome-fields'] as const) : []),\n ...(traceQueryOutcomeFieldsSupported ? (['trace-query-outcome-fields'] as const) : []),\n];\n\nexport const timingFeatures = [\n ...(timeToFirstChunkSupported ? (['trace-query-time-to-first-chunk'] as const) : []),\n ...(timeToFirstChunkSortSupported ? (['trace-query-time-to-first-chunk-sort'] as const) : []),\n ...(timeToFirstTokenSupported ? (['trace-query-time-to-first-token'] as const) : []),\n ...(timeToFirstTokenSortSupported ? (['trace-query-time-to-first-token-sort'] as const) : []),\n] as const;\n"],"mappings":"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"}
@@ -44,6 +44,10 @@ var DuckDBConnection = class extends MastraBase {
44
44
  instance = null;
45
45
  initialized = false;
46
46
  initPromise = null;
47
+ closed = false;
48
+ activeConnections = 0;
49
+ drainPromise = null;
50
+ finishDrain = null;
47
51
  path;
48
52
  instanceOptions;
49
53
  constructor(config = {}) {
@@ -58,6 +62,7 @@ var DuckDBConnection = class extends MastraBase {
58
62
  };
59
63
  }
60
64
  async initialize() {
65
+ if (this.closed) throw new Error("DuckDB connection is closed");
61
66
  if (this.initialized && this.instance) return;
62
67
  if (this.initPromise) {
63
68
  await this.initPromise;
@@ -81,8 +86,46 @@ var DuckDBConnection = class extends MastraBase {
81
86
  /** Create a new connection to the DuckDB instance, initializing if needed. */
82
87
  async getConnection() {
83
88
  await this.initialize();
89
+ if (this.closed) throw new Error("DuckDB connection is closed");
84
90
  if (!this.instance) throw new Error("DuckDB instance not initialized");
85
- return this.instance.connect();
91
+ this.activeConnections++;
92
+ try {
93
+ const connection = await this.instance.connect();
94
+ let released = false;
95
+ const release = () => {
96
+ if (!released) {
97
+ released = true;
98
+ this.releaseConnection();
99
+ }
100
+ };
101
+ return new Proxy(connection, { get(target, key) {
102
+ const value = Reflect.get(target, key, target);
103
+ if (typeof value !== "function") return value;
104
+ if (key === "closeSync" || key === "disconnectSync" || key === "close" || key === "disconnect") return (...args) => {
105
+ try {
106
+ const result = value.apply(target, args);
107
+ if (result instanceof Promise) return result.finally(release);
108
+ release();
109
+ return result;
110
+ } catch (error) {
111
+ release();
112
+ throw error;
113
+ }
114
+ };
115
+ return value.bind(target);
116
+ } });
117
+ } catch (error) {
118
+ this.releaseConnection();
119
+ throw error;
120
+ }
121
+ }
122
+ releaseConnection() {
123
+ this.activeConnections--;
124
+ if (this.activeConnections === 0) {
125
+ this.finishDrain?.();
126
+ this.finishDrain = null;
127
+ this.drainPromise = null;
128
+ }
86
129
  }
87
130
  closeConnection(connection) {
88
131
  const conn = connection;
@@ -243,6 +286,12 @@ var DuckDBConnection = class extends MastraBase {
243
286
  }
244
287
  /** Release the DuckDB instance, allowing garbage collection. */
245
288
  async close() {
289
+ this.closed = true;
290
+ await this.initPromise?.catch(() => void 0);
291
+ if (this.activeConnections > 0) {
292
+ this.drainPromise ??= new Promise((resolve) => this.finishDrain = resolve);
293
+ await this.drainPromise;
294
+ }
246
295
  if (this.instance) {
247
296
  try {
248
297
  const instance = this.instance;
@@ -257,13 +306,29 @@ var DuckDBConnection = class extends MastraBase {
257
306
  };
258
307
  // src/storage/features.ts
259
308
  const threadQueryFeatures = typeof coreStorage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [];
260
- const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? ["span-query", "span-query-context-fields"] : [];
261
- const spanAggregateFeatures = typeof coreStorage.planSpanAggregate === "function" ? ["span-aggregate"] : [];
309
+ const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? [
310
+ "span-query",
311
+ "span-query-context-fields",
312
+ ..."model" in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {}) ? ["span-query-model-fields"] : []
313
+ ] : [];
314
+ const spanAggregateFeatures = typeof coreStorage.planSpanAggregate === "function" ? ["span-aggregate", ..."model" in (coreStorage.SPAN_AGGREGATE_DIMENSION_REGISTRY ?? {}) ? ["span-aggregate-model-dimensions"] : []] : [];
262
315
  /** Core versions without the field reject it in trace query results, so it is only returned when core knows it. */
263
316
  const timeToFirstChunkSupported = "timeToFirstChunkMs" in coreStorage.TRACE_QUERY_FIELD_REGISTRY.trace;
264
317
  /** Core before the sort rejects `orderBy` on the field, so the sort is only declared when core plans it. */
265
318
  const timeToFirstChunkSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstChunkMs") === true;
266
- const timeToFirstChunkFeatures = [...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [], ...timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : []];
267
- export { timeToFirstChunkSupported as a, bindParam as c, timeToFirstChunkFeatures as i, spanQueryFeatures as n, DuckDBConnection as o, threadQueryFeatures as r, DuckDBQueryTimeoutError as s, spanAggregateFeatures as t };
319
+ /** Same rule for time to first token: returned and sorted only when core knows the field. */
320
+ const timeToFirstTokenSupported = "timeToFirstTokenMs" in coreStorage.TRACE_QUERY_FIELD_REGISTRY.trace;
321
+ const timeToFirstTokenSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.includes("timeToFirstTokenMs") === true;
322
+ /** Core before the outcome fields rejects their predicates and strict rows, so they are only returned and declared when core knows them. */
323
+ const spanQueryOutcomeFieldsSupported = "outcome" in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {});
324
+ const traceQueryOutcomeFieldsSupported = "outcome" in (coreStorage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
325
+ const outcomeFeatures = [...spanQueryOutcomeFieldsSupported ? ["span-query-outcome-fields"] : [], ...traceQueryOutcomeFieldsSupported ? ["trace-query-outcome-fields"] : []];
326
+ const timingFeatures = [
327
+ ...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [],
328
+ ...timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : [],
329
+ ...timeToFirstTokenSupported ? ["trace-query-time-to-first-token"] : [],
330
+ ...timeToFirstTokenSortSupported ? ["trace-query-time-to-first-token-sort"] : []
331
+ ];
332
+ export { threadQueryFeatures as a, timingFeatures as c, DuckDBQueryTimeoutError as d, bindParam as f, spanQueryOutcomeFieldsSupported as i, traceQueryOutcomeFieldsSupported as l, spanAggregateFeatures as n, timeToFirstChunkSupported as o, spanQueryFeatures as r, timeToFirstTokenSupported as s, outcomeFeatures as t, DuckDBConnection as u };
268
333
 
269
- //# sourceMappingURL=features-B8gl38Ue.js.map
334
+ //# sourceMappingURL=features-D9RrctvA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features-D9RrctvA.js","names":[],"sources":["../src/storage/db/index.ts","../src/storage/features.ts"],"sourcesContent":["import { DuckDBInstance, DuckDBTimestampValue, DuckDBTimestampTZValue } from '@duckdb/node-api';\nimport type { DuckDBPreparedStatement } from '@duckdb/node-api';\nimport { MastraBase } from '@mastra/core/base';\n\n/**\n * Bind a single parameter to a prepared statement using explicit typed methods.\n * This avoids the \"Cannot create values of type ANY\" error that occurs when\n * DuckDB cannot infer parameter types from SQL context (e.g. json_extract_string).\n */\nexport function bindParam(stmt: DuckDBPreparedStatement, index: number, value: unknown): void {\n if (value === null || value === undefined) {\n stmt.bindNull(index);\n } else if (typeof value === 'string') {\n stmt.bindVarchar(index, value);\n } else if (typeof value === 'number') {\n if (Number.isInteger(value) && value >= -2147483648 && value <= 2147483647) {\n stmt.bindInteger(index, value);\n } else {\n stmt.bindDouble(index, value);\n }\n } else if (typeof value === 'boolean') {\n stmt.bindBoolean(index, value);\n } else if (typeof value === 'bigint') {\n stmt.bindBigInt(index, value);\n } else if (value instanceof Date) {\n stmt.bindTimestamp(index, new DuckDBTimestampValue(BigInt(value.getTime()) * 1000n));\n } else if (value instanceof DuckDBTimestampValue) {\n stmt.bindTimestamp(index, value);\n } else if (value instanceof DuckDBTimestampTZValue) {\n stmt.bindTimestampTZ(index, value);\n } else {\n // Fallback: serialize to JSON string\n stmt.bindVarchar(index, JSON.stringify(value));\n }\n}\n\n/** Convert DuckDB-specific return types to plain JS types */\nfunction toJsValue(val: unknown): unknown {\n if (val === null || val === undefined) return val;\n // DuckDBTimestampValue → Date (micros since epoch)\n if (val instanceof DuckDBTimestampValue) {\n return new Date(Number(val.micros / 1000n));\n }\n // BigInt → Number (safe for values we care about)\n if (typeof val === 'bigint') {\n return Number(val);\n }\n return val;\n}\n\n/** A query interrupted at its caller-supplied deadline. */\nexport class DuckDBQueryTimeoutError extends Error {\n constructor() {\n super('DuckDB query exceeded its deadline');\n this.name = 'DuckDBQueryTimeoutError';\n }\n}\n\n/** Configuration for the DuckDB database connection. */\nexport interface DuckDBStorageConfig {\n /** Path to the DuckDB file. Defaults to 'mastra.duckdb'. Use ':memory:' for ephemeral. */\n path?: string;\n /**\n * Maximum memory DuckDB may use (e.g. '2GB', '512MB').\n * @default '2GB'\n * DuckDB's own default is 80% of system RAM, which is far too aggressive for\n * a store embedded in an application server — a single query against a large\n * database can balloon the process by several GB and push the host into\n * swap. Larger-than-memory operations spill to disk for file-backed\n * databases. Raise this for dedicated analytical workloads.\n */\n memoryLimit?: string;\n /**\n * Number of threads DuckDB may use. Defaults to DuckDB's default (one per\n * CPU core). Lower this to keep queries from monopolizing all cores of a\n * shared application server.\n */\n threads?: number;\n}\n\nconst DEFAULT_MEMORY_LIMIT = '2GB';\n\n/**\n * Shared DuckDB connection management for Mastra storage.\n * Defaults to a local file (`mastra.duckdb`) when no path is provided.\n * Pass `path: ':memory:'` for an ephemeral in-memory database.\n */\nexport class DuckDBConnection extends MastraBase {\n private instance: DuckDBInstance | null = null;\n private initialized = false;\n private initPromise: Promise<void> | null = null;\n private closed = false;\n private activeConnections = 0;\n private drainPromise: Promise<void> | null = null;\n private finishDrain: (() => void) | null = null;\n private path: string;\n private instanceOptions: Record<string, string>;\n\n constructor(config: DuckDBStorageConfig = {}) {\n super({ component: 'STORAGE', name: 'DUCKDB' });\n this.path = config.path ?? 'mastra.duckdb';\n this.instanceOptions = {\n max_memory: config.memoryLimit ?? DEFAULT_MEMORY_LIMIT,\n ...(config.threads !== undefined ? { threads: String(config.threads) } : {}),\n };\n }\n\n private async initialize(): Promise<void> {\n if (this.closed) throw new Error('DuckDB connection is closed');\n if (this.initialized && this.instance) return;\n\n if (this.initPromise) {\n await this.initPromise;\n if (this.instance) return;\n this.initPromise = null;\n this.initialized = false;\n }\n\n this.initPromise = (async () => {\n try {\n this.instance = await DuckDBInstance.create(this.path, this.instanceOptions);\n this.initialized = true;\n } catch (error) {\n this.instance = null;\n this.initialized = false;\n this.initPromise = null;\n throw error;\n }\n })();\n\n return this.initPromise;\n }\n\n /** Create a new connection to the DuckDB instance, initializing if needed. */\n async getConnection() {\n await this.initialize();\n if (this.closed) throw new Error('DuckDB connection is closed');\n if (!this.instance) {\n throw new Error('DuckDB instance not initialized');\n }\n this.activeConnections++;\n try {\n const connection = await this.instance.connect();\n let released = false;\n const release = () => {\n if (!released) {\n released = true;\n this.releaseConnection();\n }\n };\n return new Proxy(connection, {\n get(target, key) {\n const value = Reflect.get(target, key, target);\n if (typeof value !== 'function') return value;\n if (key === 'closeSync' || key === 'disconnectSync' || key === 'close' || key === 'disconnect') {\n return (...args: unknown[]) => {\n try {\n const result = value.apply(target, args);\n if (result instanceof Promise) return result.finally(release);\n release();\n return result;\n } catch (error) {\n release();\n throw error;\n }\n };\n }\n return value.bind(target);\n },\n });\n } catch (error) {\n this.releaseConnection();\n throw error;\n }\n }\n\n private releaseConnection(): void {\n this.activeConnections--;\n if (this.activeConnections === 0) {\n this.finishDrain?.();\n this.finishDrain = null;\n this.drainPromise = null;\n }\n }\n\n private closeConnection(connection: unknown): void {\n const conn = connection as {\n closeSync?: () => void;\n disconnectSync?: () => void;\n close?: () => void;\n disconnect?: () => void;\n };\n try {\n if (typeof conn?.closeSync === 'function') {\n conn.closeSync();\n return;\n }\n if (typeof conn?.disconnectSync === 'function') {\n conn.disconnectSync();\n return;\n }\n if (typeof conn?.close === 'function') {\n conn.close();\n return;\n }\n if (typeof conn?.disconnect === 'function') {\n conn.disconnect();\n }\n } catch {\n // Ignore close failures to avoid masking query/execute errors.\n }\n }\n\n /**\n * Execute a SQL query and return results as objects.\n */\n async query<T = Record<string, unknown>>(sql: string, params: unknown[] = [], timeoutMs?: number): Promise<T[]> {\n const connection = await this.getConnection();\n let timedOut = false;\n const timer =\n timeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n timedOut = true;\n connection.interrupt();\n }, timeoutMs);\n try {\n if (params.length === 0) {\n const result = await connection.run(sql);\n const rows = await result.getRows();\n const columns = result.columnNames();\n return rows.map(row => {\n const obj: Record<string, unknown> = {};\n columns.forEach((col, i) => {\n obj[col] = toJsValue(row[i]);\n });\n return obj as T;\n });\n }\n\n let paramIndex = 0;\n const preparedSql = sql.replace(/\\?/g, () => `$${++paramIndex}`);\n const stmt = await connection.prepare(preparedSql);\n for (let i = 0; i < params.length; i++) {\n bindParam(stmt, i + 1, params[i]);\n }\n const result = await stmt.run();\n const rows = await result.getRows();\n const columns = result.columnNames();\n return rows.map(row => {\n const obj: Record<string, unknown> = {};\n columns.forEach((col, i) => {\n obj[col] = toJsValue(row[i]);\n });\n return obj as T;\n });\n } catch (error) {\n if (timedOut) throw new DuckDBQueryTimeoutError();\n throw error;\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n this.closeConnection(connection);\n }\n }\n\n /**\n * Execute a SQL statement without returning results.\n */\n async execute(sql: string, params: unknown[] = []): Promise<void> {\n const connection = await this.getConnection();\n try {\n if (params.length === 0) {\n await connection.run(sql);\n return;\n }\n let paramIndex = 0;\n const preparedSql = sql.replace(/\\?/g, () => `$${++paramIndex}`);\n const stmt = await connection.prepare(preparedSql);\n for (let i = 0; i < params.length; i++) {\n bindParam(stmt, i + 1, params[i]);\n }\n await stmt.run();\n } finally {\n this.closeConnection(connection);\n }\n }\n\n /** Delete one bounded retention batch and return the number of rows removed. */\n async pruneBatch({\n tableName,\n column,\n cutoff,\n limit,\n }: {\n tableName: string;\n column: string;\n cutoff: Date;\n limit: number;\n }): Promise<number> {\n const identifier = /^[A-Za-z_][A-Za-z0-9_]*$/;\n if (!identifier.test(tableName) || !identifier.test(column)) {\n throw new Error(`Invalid retention identifier: ${tableName}.${column}`);\n }\n\n const rows = await this.query(\n `DELETE FROM ${tableName}\n WHERE rowid IN (\n SELECT rowid FROM ${tableName}\n WHERE ${column} < ?\n ORDER BY ${column}\n LIMIT ?\n )\n RETURNING 1 AS deleted`,\n [cutoff, limit],\n );\n return rows.length;\n }\n\n /** Execute parameterized statements atomically using a single DuckDB connection. */\n async executeTransaction(statements: readonly { sql: string; params?: readonly unknown[] }[]): Promise<void> {\n if (statements.length === 0) return;\n\n const connection = await this.getConnection();\n try {\n await connection.run('BEGIN TRANSACTION');\n for (const statement of statements) {\n const params = statement.params ?? [];\n if (params.length === 0) {\n await connection.run(statement.sql);\n continue;\n }\n let paramIndex = 0;\n const preparedSql = statement.sql.replace(/\\?/g, () => `$${++paramIndex}`);\n const prepared = await connection.prepare(preparedSql);\n for (let i = 0; i < params.length; i++) {\n bindParam(prepared, i + 1, params[i]);\n }\n await prepared.run();\n }\n await connection.run('COMMIT');\n } catch (error) {\n await connection.run('ROLLBACK').catch(() => undefined);\n throw error;\n } finally {\n this.closeConnection(connection);\n }\n }\n\n /**\n * Execute multiple SQL statements in order using a single DuckDB connection.\n *\n * This is intended for schema setup/migrations where statements have no\n * parameters and must remain ordered, but opening a connection per statement\n * would dominate initialization cost. Blank statements are skipped. Like\n * calling execute() repeatedly, this does not wrap statements in a transaction,\n * so prior statements can remain applied if a later statement fails.\n */\n async executeBatch(sqlStatements: readonly string[]): Promise<void> {\n const statements = sqlStatements.map(statement => statement.trim()).filter(Boolean);\n if (statements.length === 0) return;\n\n const connection = await this.getConnection();\n try {\n const sql =\n statements.map((statement, i) => `-- executeBatch statement ${i + 1}\\n${statement}`).join('\\n;\\n') + '\\n;';\n await connection.run(sql);\n } finally {\n this.closeConnection(connection);\n }\n }\n\n /**\n * Escape a value for safe inline SQL use.\n * DuckDB prepared statements can't handle NULL for parameters typed as ANY,\n * so for complex INSERT/UPDATE operations we inline values safely.\n */\n static sqlValue(value: unknown): string {\n if (value === null || value === undefined) return 'NULL';\n if (typeof value === 'number') {\n if (!Number.isFinite(value)) return 'NULL';\n return String(value);\n }\n if (typeof value === 'boolean') return value ? 'TRUE' : 'FALSE';\n if (value instanceof Date) return `'${value.toISOString()}'::TIMESTAMP`;\n if (typeof value === 'string') return `'${value.replace(/'/g, \"''\")}'`;\n // Objects/arrays → JSON string\n return `'${JSON.stringify(value).replace(/'/g, \"''\")}'`;\n }\n\n /** Release the DuckDB instance, allowing garbage collection. */\n async close(): Promise<void> {\n this.closed = true;\n await this.initPromise?.catch(() => undefined);\n if (this.activeConnections > 0) {\n this.drainPromise ??= new Promise<void>(resolve => (this.finishDrain = resolve));\n await this.drainPromise;\n }\n if (this.instance) {\n try {\n const instance = this.instance as unknown as { closeSync?: () => void; close?: () => void };\n if (typeof instance.closeSync === 'function') {\n instance.closeSync();\n } else if (typeof instance.close === 'function') {\n instance.close();\n }\n } catch {\n // Ignore close failures to allow cleanup of references.\n }\n this.instance = null;\n this.initialized = false;\n this.initPromise = null;\n }\n }\n}\n","import * as coreStorage from '@mastra/core/storage';\n\nexport const threadQueryFeatures =\n typeof coreStorage.planThreadRowsQuery === 'function' ? (['thread-query-rows'] as const) : ([] as const);\n\nexport const spanQueryFeatures =\n typeof coreStorage.planSpanQuery === 'function'\n ? ([\n 'span-query',\n 'span-query-context-fields',\n // Core before the fields rejects `model`/`provider` predicates, so only declare them when core plans them.\n ...('model' in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {}) ? (['span-query-model-fields'] as const) : []),\n ] as const)\n : ([] as const);\n\nexport const spanAggregateFeatures =\n typeof coreStorage.planSpanAggregate === 'function'\n ? ([\n 'span-aggregate',\n // Core before the dimensions rejects `model`/`provider` in `groupBy`, so only declare them when core plans them.\n ...('model' in (coreStorage.SPAN_AGGREGATE_DIMENSION_REGISTRY ?? {})\n ? (['span-aggregate-model-dimensions'] as const)\n : []),\n ] as const)\n : ([] as const);\n\n/** Core versions without the field reject it in trace query results, so it is only returned when core knows it. */\nexport const timeToFirstChunkSupported = 'timeToFirstChunkMs' in coreStorage.TRACE_QUERY_FIELD_REGISTRY.trace;\n\n/** Core before the sort rejects `orderBy` on the field, so the sort is only declared when core plans it. */\nconst timeToFirstChunkSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.includes('timeToFirstChunkMs') === true;\n\n/** Same rule for time to first token: returned and sorted only when core knows the field. */\nexport const timeToFirstTokenSupported = 'timeToFirstTokenMs' in coreStorage.TRACE_QUERY_FIELD_REGISTRY.trace;\nconst timeToFirstTokenSortSupported = coreStorage.TRACE_QUERY_ORDER_FIELDS?.includes('timeToFirstTokenMs') === true;\n\n/** Core before the outcome fields rejects their predicates and strict rows, so they are only returned and declared when core knows them. */\nexport const spanQueryOutcomeFieldsSupported = 'outcome' in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {});\nexport const traceQueryOutcomeFieldsSupported = 'outcome' in (coreStorage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});\n\nexport const outcomeFeatures: readonly ('span-query-outcome-fields' | 'trace-query-outcome-fields')[] = [\n ...(spanQueryOutcomeFieldsSupported ? (['span-query-outcome-fields'] as const) : []),\n ...(traceQueryOutcomeFieldsSupported ? (['trace-query-outcome-fields'] as const) : []),\n];\n\nexport const timingFeatures = [\n ...(timeToFirstChunkSupported ? (['trace-query-time-to-first-chunk'] as const) : []),\n ...(timeToFirstChunkSortSupported ? (['trace-query-time-to-first-chunk-sort'] as const) : []),\n ...(timeToFirstTokenSupported ? (['trace-query-time-to-first-token'] as const) : []),\n ...(timeToFirstTokenSortSupported ? (['trace-query-time-to-first-token-sort'] as const) : []),\n] as const;\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_features = require("./features-DH60yLCA.cjs");
2
+ const require_features = require("./features-CBdrOdEO.cjs");
3
3
  let _duckdb_node_api = require("@duckdb/node-api");
4
4
  let _mastra_core_error = require("@mastra/core/error");
5
5
  let _mastra_core_storage = require("@mastra/core/storage");
@@ -505,7 +505,8 @@ const DUCKDB_OBSERVABILITY_FEATURES = [
505
505
  "trace-query-tenant-scope",
506
506
  "feedback",
507
507
  "trace-query-context-ids",
508
- ...require_features.timeToFirstChunkFeatures,
508
+ ...require_features.timingFeatures,
509
+ ...require_features.outcomeFeatures,
509
510
  "trace-query-root-span-fields",
510
511
  "trace-query-root-entity-id"
511
512
  ];
@@ -530,7 +531,8 @@ const DUCKDB_OBSERVABILITY_DELTA_FEATURES = [
530
531
  "trace-query-tenant-scope",
531
532
  "feedback",
532
533
  "trace-query-context-ids",
533
- ...require_features.timeToFirstChunkFeatures,
534
+ ...require_features.timingFeatures,
535
+ ...require_features.outcomeFeatures,
534
536
  "trace-query-root-span-fields",
535
537
  "trace-query-root-entity-id"
536
538
  ];
@@ -549,6 +551,8 @@ var ObservabilityStorageDuckDB = class extends _mastra_core_storage.Observabilit
549
551
  delegate = null;
550
552
  loadPromise = null;
551
553
  unavailableError = null;
554
+ pendingInits = /* @__PURE__ */ new Set();
555
+ closed = false;
552
556
  constructor(config) {
553
557
  super();
554
558
  this.db = config.db;
@@ -564,7 +568,7 @@ var ObservabilityStorageDuckDB = class extends _mastra_core_storage.Observabilit
564
568
  async loadDelegate() {
565
569
  if (this.delegate) return this.delegate;
566
570
  if (this.unavailableError) return null;
567
- if (!this.loadPromise) this.loadPromise = Promise.resolve().then(() => require("./observability-BEdkwaeY.cjs")).then(({ ObservabilityStorageDuckDB }) => {
571
+ if (!this.loadPromise) this.loadPromise = Promise.resolve().then(() => require("./observability-B_ARB4Uv.cjs")).then(({ ObservabilityStorageDuckDB }) => {
568
572
  const delegate = new ObservabilityStorageDuckDB({ db: this.db });
569
573
  this.delegate = delegate;
570
574
  return delegate;
@@ -596,9 +600,22 @@ var ObservabilityStorageDuckDB = class extends _mastra_core_storage.Observabilit
596
600
  return DUCKDB_OBSERVABILITY_DELTA_FEATURES;
597
601
  }
598
602
  async init(...args) {
599
- const delegate = await this.loadDelegate();
600
- if (!delegate) return;
601
- return delegate.init(...args);
603
+ if (this.closed) throw new Error("DuckDB observability storage is closed");
604
+ const init = (async () => {
605
+ const delegate = await this.loadDelegate();
606
+ if (delegate && !this.closed) await delegate.init(...args);
607
+ })();
608
+ this.pendingInits.add(init);
609
+ try {
610
+ await init;
611
+ } finally {
612
+ this.pendingInits.delete(init);
613
+ }
614
+ }
615
+ async close() {
616
+ this.closed = true;
617
+ await Promise.allSettled(this.pendingInits);
618
+ await this.db.close();
602
619
  }
603
620
  async migrateSpans(...args) {
604
621
  return (await this.requireDelegate()).migrateSpans(...args);
@@ -825,7 +842,7 @@ var DuckDBStore = class extends _mastra_core_storage.MastraCompositeStore {
825
842
  * Safe to call more than once; subsequent calls are no-ops.
826
843
  */
827
844
  async close() {
828
- await this.db.close();
845
+ await this.observabilityStore.close();
829
846
  }
830
847
  };
831
848
  exports.DuckDBConnection = require_features.DuckDBConnection;