@mastra/pg 1.22.3 → 1.23.0-alpha.0

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-pg
3
3
  description: Documentation for @mastra/pg. Use when working with @mastra/pg APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/pg"
6
- version: "1.22.3"
6
+ version: "1.23.0-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.22.3",
2
+ "version": "1.23.0-alpha.0",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -285,6 +285,8 @@ curl http://localhost:4111/api/agents
285
285
 
286
286
  ### Kubernetes
287
287
 
288
+ > **Helm:** On Kubernetes, the [`mastra-projects` Helm chart](https://mastra.ai/integrations/deploy/kubernetes-helm) renders these deployments for you, with the workers on the same image and environment as the server. It also refuses to install combinations that would run every worker twice. The manifests below are for writing them by hand.
289
+
288
290
  Create separate Deployments for the API, orchestration worker, scheduler worker, and background task worker. Use the same image and Secret for each Deployment. Set only the role-specific environment variables directly on each container.
289
291
 
290
292
  The orchestration worker Deployment has the following shape:
@@ -391,4 +393,5 @@ If the API crashes while a step is executing, that work can be lost and the work
391
393
  - [Workers reference](https://mastra.ai/reference/workers/overview): Details about worker environment variables and types, with a list of supported storage backends
392
394
  - [CLI reference](https://mastra.ai/reference/cli/mastra): `mastra worker build` and `mastra worker start`
393
395
  - [PubSub](https://mastra.ai/docs/server/pubsub): Event delivery backends
396
+ - [Kubernetes (Helm)](https://mastra.ai/integrations/deploy/kubernetes-helm): Deploy worker roles with the official chart
394
397
  - [Scheduled workflows](https://mastra.ai/docs/workflows/scheduled-workflows): Declare cron schedules on workflows
@@ -205,6 +205,7 @@ Each provider page includes installation instructions, configuration parameters,
205
205
  - [libSQL](https://mastra.ai/integrations/databases/libsql)
206
206
  - [Mastra](https://mastra.ai/docs/mastra-platform/database)
207
207
  - [MongoDB](https://mastra.ai/integrations/databases/mongodb)
208
+ - [MySQL](https://mastra.ai/integrations/databases/mysql)
208
209
  - [MSSQL](https://mastra.ai/integrations/databases/mssql)
209
210
  - [Neon Postgres](https://mastra.ai/integrations/databases/neon)
210
211
  - [OracleDB](https://mastra.ai/integrations/databases/oracledb)
@@ -176,6 +176,7 @@ const results = await store.query({
176
176
  - Special handling for null and empty values
177
177
  - Vector-specific filtering capabilities
178
178
  - Datetime values must be in RFC 3339 format
179
+ - `$regex` is translated to Qdrant's full-text match, which matches whole tokens rather than arbitrary regular expressions or substrings (e.g. `{ name: { $regex: "item" } }` doesn't match `"item1"`). On unindexed payload fields, Qdrant versions before v1.19.1 fall back to substring matching
179
180
 
180
181
  ### Upstash
181
182
 
package/dist/index.cjs CHANGED
@@ -23,6 +23,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  //#endregion
24
24
  let _mastra_core_error = require("@mastra/core/error");
25
25
  let _mastra_core_storage = require("@mastra/core/storage");
26
+ _mastra_core_storage = __toESM(_mastra_core_storage, 1);
26
27
  let _mastra_core_utils = require("@mastra/core/utils");
27
28
  let _mastra_core_vector = require("@mastra/core/vector");
28
29
  let async_mutex = require("async-mutex");
@@ -12169,12 +12170,20 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12169
12170
  };
12170
12171
  const lastBufferedAtTime = input.lastBufferedAtTime ? input.lastBufferedAtTime.toISOString() : null;
12171
12172
  if ((await this.#db.client.query(`UPDATE ${tableName} SET
12172
- "bufferedObservationChunks" = COALESCE("bufferedObservationChunks", '[]'::jsonb) || $1::jsonb,
12173
- "lastBufferedAtTime" = COALESCE($2, "lastBufferedAtTime"),
12174
- "updatedAt" = $3,
12175
- "updatedAtZ" = $4
12176
- WHERE id = $5`, [
12173
+ "bufferedObservationChunks" = CASE
12174
+ WHEN EXISTS (
12175
+ SELECT 1
12176
+ FROM jsonb_array_elements(COALESCE("bufferedObservationChunks", '[]'::jsonb)) AS chunk
12177
+ WHERE chunk->>'cycleId' = $2
12178
+ ) THEN COALESCE("bufferedObservationChunks", '[]'::jsonb)
12179
+ ELSE COALESCE("bufferedObservationChunks", '[]'::jsonb) || $1::jsonb
12180
+ END,
12181
+ "lastBufferedAtTime" = COALESCE($3, "lastBufferedAtTime"),
12182
+ "updatedAt" = $4,
12183
+ "updatedAtZ" = $5
12184
+ WHERE id = $6`, [
12177
12185
  JSON.stringify([newChunk]),
12186
+ input.chunk.cycleId,
12178
12187
  lastBufferedAtTime,
12179
12188
  nowStr,
12180
12189
  nowStr,
@@ -16739,6 +16748,304 @@ async function getScorePercentiles(client, schema, args) {
16739
16748
  return { series: percentileSeriesFromRows(await client.manyOrNone(sql, acc.params), args.percentiles) };
16740
16749
  }
16741
16750
  //#endregion
16751
+ //#region src/storage/domains/observability/v-next/trace-query.ts
16752
+ const TRACE_STATUS_SQL = `CASE WHEN r."error" IS NOT NULL THEN 'error' ELSE 'success' END`;
16753
+ const TRACE_FIELDS = {
16754
+ traceId: "r.\"traceId\"",
16755
+ threadId: "r.\"threadId\"",
16756
+ resourceId: "r.\"resourceId\"",
16757
+ startedAt: "r.\"startedAt\"",
16758
+ endedAt: "r.\"endedAt\"",
16759
+ entityName: "r.\"entityName\"",
16760
+ entityType: "r.\"entityType\"",
16761
+ environment: "r.\"environment\"",
16762
+ status: TRACE_STATUS_SQL
16763
+ };
16764
+ const SPAN_FIELDS = {
16765
+ spanType: "s.\"spanType\"",
16766
+ error: "s.\"error\""
16767
+ };
16768
+ const SCORE_FIELDS = {
16769
+ scorerId: "s.\"scorerId\"",
16770
+ score: "s.\"score\""
16771
+ };
16772
+ const TRACE_SELECT = `
16773
+ r."traceId" AS "traceId",
16774
+ r."spanId" AS "rootSpanId",
16775
+ r."threadId" AS "threadId",
16776
+ r."resourceId" AS "resourceId",
16777
+ r."startedAt" AS "startedAt",
16778
+ r."endedAt" AS "endedAt",
16779
+ r."entityName" AS "entityName",
16780
+ r."entityType" AS "entityType",
16781
+ r."environment" AS "environment",
16782
+ ${TRACE_STATUS_SQL} AS "status"`;
16783
+ function fieldSql(registry, field) {
16784
+ const sql = registry[field];
16785
+ if (sql === void 0) throw new Error(`Unsupported trusted trace-query field: ${field}`);
16786
+ return sql;
16787
+ }
16788
+ function placeholders(values, offset) {
16789
+ return values.map((_, index) => `$${offset + index}`).join(", ");
16790
+ }
16791
+ function compileScalarPredicate(predicate, registry, parameterOffset) {
16792
+ if (predicate.type === "boolean") {
16793
+ const values = [];
16794
+ return {
16795
+ sql: predicate.args.map((arg) => {
16796
+ const compiled = compileScalarPredicate(arg, registry, parameterOffset + values.length);
16797
+ values.push(...compiled.values);
16798
+ return `(${compiled.sql})`;
16799
+ }).join(predicate.operator === "and" ? " AND " : " OR "),
16800
+ values
16801
+ };
16802
+ }
16803
+ if (predicate.type === "not") {
16804
+ const compiled = compileScalarPredicate(predicate.arg, registry, parameterOffset);
16805
+ return {
16806
+ sql: `NOT (${compiled.sql})`,
16807
+ values: compiled.values
16808
+ };
16809
+ }
16810
+ const field = fieldSql(registry, predicate.field);
16811
+ if (predicate.type === "presence") return {
16812
+ sql: `${field} IS ${predicate.operator === "exists" ? "NOT " : ""}NULL`,
16813
+ values: []
16814
+ };
16815
+ if (predicate.type === "membership") {
16816
+ const list = placeholders(predicate.values, parameterOffset);
16817
+ if (predicate.operator === "in") return {
16818
+ sql: `${field} IS NOT NULL AND ${field} IN (${list})`,
16819
+ values: predicate.values
16820
+ };
16821
+ return {
16822
+ sql: `${field} IS NULL OR ${field} NOT IN (${list})`,
16823
+ values: predicate.values
16824
+ };
16825
+ }
16826
+ const parameter = `$${parameterOffset}`;
16827
+ const operators = {
16828
+ lt: "<",
16829
+ lte: "<=",
16830
+ gt: ">",
16831
+ gte: ">="
16832
+ };
16833
+ if (predicate.operator === "eq") return {
16834
+ sql: `${field} IS NOT DISTINCT FROM ${parameter}`,
16835
+ values: [predicate.value]
16836
+ };
16837
+ if (predicate.operator === "ne") return {
16838
+ sql: `${field} IS DISTINCT FROM ${parameter}`,
16839
+ values: [predicate.value]
16840
+ };
16841
+ const operator = operators[predicate.operator];
16842
+ if (operator === void 0) throw new Error(`Unsupported trusted trace-query operator: ${predicate.operator}`);
16843
+ return {
16844
+ sql: `${field} IS NOT NULL AND ${field} ${operator} ${parameter}`,
16845
+ values: [predicate.value]
16846
+ };
16847
+ }
16848
+ function latestRootPredicate$1(spanTable) {
16849
+ return `NOT EXISTS (
16850
+ SELECT 1 FROM ${spanTable} newer
16851
+ WHERE newer."traceId" = r."traceId"
16852
+ AND newer."parentSpanId" IS NULL
16853
+ AND newer."cursorId" > r."cursorId"
16854
+ )`;
16855
+ }
16856
+ function latestSpanPredicate$1(spanTable) {
16857
+ return `NOT EXISTS (
16858
+ SELECT 1 FROM ${spanTable} newer
16859
+ WHERE newer."traceId" = s."traceId"
16860
+ AND newer."spanId" = s."spanId"
16861
+ AND (newer."isPending" < s."isPending" OR (newer."isPending" = s."isPending" AND newer."cursorId" > s."cursorId"))
16862
+ )`;
16863
+ }
16864
+ function latestScorePredicate(scoreTable) {
16865
+ return `NOT EXISTS (
16866
+ SELECT 1 FROM ${scoreTable} newer
16867
+ WHERE newer."scoreId" = s."scoreId"
16868
+ AND newer."cursorId" > s."cursorId"
16869
+ )`;
16870
+ }
16871
+ function collectRelationCollections(predicate, collections = /* @__PURE__ */ new Set()) {
16872
+ if (!predicate) return collections;
16873
+ if (predicate.type === "relation") collections.add(predicate.collection);
16874
+ else if (predicate.type === "boolean") for (const arg of predicate.args) collectRelationCollections(arg, collections);
16875
+ else if (predicate.type === "not") collectRelationCollections(predicate.arg, collections);
16876
+ return collections;
16877
+ }
16878
+ function compilePredicate(predicate, parameterOffset) {
16879
+ if (predicate.type === "relation") {
16880
+ const registry = predicate.collection === "spans" ? SPAN_FIELDS : SCORE_FIELDS;
16881
+ const compiled = compileScalarPredicate(predicate.predicate, registry, parameterOffset);
16882
+ const existence = `EXISTS (
16883
+ SELECT 1 FROM ${predicate.collection === "spans" ? "current_spans" : "current_scores"} s
16884
+ WHERE s."traceId" = r."traceId"
16885
+ AND (${compiled.sql})
16886
+ )`;
16887
+ return {
16888
+ sql: predicate.quantifier === "some" ? existence : `NOT ${existence}`,
16889
+ values: compiled.values
16890
+ };
16891
+ }
16892
+ if (predicate.type === "boolean") {
16893
+ const values = [];
16894
+ return {
16895
+ sql: predicate.args.map((arg) => {
16896
+ const compiled = compilePredicate(arg, parameterOffset + values.length);
16897
+ values.push(...compiled.values);
16898
+ return `(${compiled.sql})`;
16899
+ }).join(predicate.operator === "and" ? " AND " : " OR "),
16900
+ values
16901
+ };
16902
+ }
16903
+ if (predicate.type === "not") {
16904
+ const compiled = compilePredicate(predicate.arg, parameterOffset);
16905
+ return {
16906
+ sql: `NOT (${compiled.sql})`,
16907
+ values: compiled.values
16908
+ };
16909
+ }
16910
+ return compileScalarPredicate(predicate, TRACE_FIELDS, parameterOffset);
16911
+ }
16912
+ function compilePostgresTraceQuery(schema, plan) {
16913
+ const spanTable = qualifiedTable(schema, TABLE_SPAN_EVENTS);
16914
+ const scoreTable = qualifiedTable(schema, TABLE_SCORE_EVENTS);
16915
+ const values = [plan.timeRange.from, plan.timeRange.to];
16916
+ const relationCollections = collectRelationCollections(plan.where);
16917
+ const ctes = [`root_scope AS MATERIALIZED (
16918
+ SELECT *
16919
+ FROM ${spanTable} r
16920
+ WHERE ${[
16921
+ `r."parentSpanId" IS NULL`,
16922
+ latestRootPredicate$1(spanTable),
16923
+ `NOT r."isPending"`,
16924
+ `r."endedAt" IS NOT NULL`,
16925
+ `r."startedAt" >= $1`,
16926
+ `r."startedAt" < $2`
16927
+ ].join("\n AND ")}
16928
+ )`];
16929
+ if (relationCollections.has("spans")) ctes.push(`current_spans AS MATERIALIZED (
16930
+ SELECT s."traceId", s."spanType", s."error"
16931
+ FROM ${spanTable} s
16932
+ WHERE s."traceId" IS NOT NULL
16933
+ AND s."traceId" IN (SELECT "traceId" FROM root_scope)
16934
+ AND ${latestSpanPredicate$1(spanTable)}
16935
+ )`);
16936
+ if (relationCollections.has("scores")) ctes.push(`current_scores AS MATERIALIZED (
16937
+ SELECT s."traceId", s."scorerId", s."score"
16938
+ FROM ${scoreTable} s
16939
+ WHERE s."traceId" IS NOT NULL
16940
+ AND s."traceId" IN (SELECT "traceId" FROM root_scope)
16941
+ AND ${latestScorePredicate(scoreTable)}
16942
+ )`);
16943
+ let predicateSql = "TRUE";
16944
+ if (plan.where) {
16945
+ const predicate = compilePredicate(plan.where, values.length + 1);
16946
+ predicateSql = predicate.sql;
16947
+ values.push(...predicate.values);
16948
+ }
16949
+ ctes.push(`candidates AS (
16950
+ SELECT ${TRACE_SELECT}
16951
+ FROM root_scope r
16952
+ WHERE ${predicateSql}
16953
+ )`);
16954
+ const candidates = `WITH ${ctes.join(",\n")}`;
16955
+ if (plan.result === "groups") {
16956
+ const pageCondition = plan.cursor ? `AND "threadId" > $${values.length + 1}` : "";
16957
+ if (plan.cursor) values.push(plan.cursor.threadId);
16958
+ values.push(plan.limit + 1);
16959
+ return {
16960
+ text: `${candidates}
16961
+ SELECT "threadId"
16962
+ FROM candidates
16963
+ WHERE "threadId" IS NOT NULL ${pageCondition}
16964
+ GROUP BY "threadId"
16965
+ ORDER BY "threadId" ASC
16966
+ LIMIT $${values.length}`,
16967
+ values
16968
+ };
16969
+ }
16970
+ const orderField = plan.orderBy.field === "startedAt" ? "\"startedAt\"" : "\"endedAt\"";
16971
+ const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
16972
+ let pageCondition = "";
16973
+ if (plan.cursor) {
16974
+ const comparison = plan.orderBy.direction === "asc" ? ">" : "<";
16975
+ const sortParameter = `$${values.length + 1}`;
16976
+ pageCondition = `WHERE (${orderField} ${comparison} ${sortParameter} OR (${orderField} = ${sortParameter} AND "traceId" > ${`$${values.length + 2}`}))`;
16977
+ values.push(plan.cursor.sortValue, plan.cursor.traceId);
16978
+ }
16979
+ values.push(plan.limit + 1);
16980
+ return {
16981
+ text: `${candidates}
16982
+ SELECT *
16983
+ FROM candidates
16984
+ ${pageCondition}
16985
+ ORDER BY ${orderField} ${direction}, "traceId" ASC
16986
+ LIMIT $${values.length}`,
16987
+ values
16988
+ };
16989
+ }
16990
+ function asIsoTimestamp$1(value) {
16991
+ if (value === null || value === void 0) throw new Error("Trace query returned a null timestamp");
16992
+ return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
16993
+ }
16994
+ function isPostgresStatementTimeout(error) {
16995
+ if (!error || typeof error !== "object") return false;
16996
+ const candidate = error;
16997
+ return candidate.code === "57014" && String(candidate.message ?? "").includes("statement timeout");
16998
+ }
16999
+ async function runWithPostgresTraceQueryTimeout(client, timeoutMs, execute) {
17000
+ const resolvedTimeoutMs = _mastra_core_storage.resolveTraceQueryTimeoutMs(timeoutMs);
17001
+ try {
17002
+ return await client.tx(async (transaction) => {
17003
+ await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${resolvedTimeoutMs}ms`]);
17004
+ return execute(transaction);
17005
+ });
17006
+ } catch (error) {
17007
+ if (isPostgresStatementTimeout(error)) throw new _mastra_core_storage.TraceQueryExecutionError();
17008
+ throw error;
17009
+ }
17010
+ }
17011
+ async function queryTraces(client, schema, plan, timeoutMs) {
17012
+ const query = compilePostgresTraceQuery(schema, plan);
17013
+ const rows = await runWithPostgresTraceQueryTimeout(client, timeoutMs, (transaction) => transaction.any(query.text, query.values));
17014
+ const visibleRows = rows.slice(0, plan.limit);
17015
+ if (plan.result === "groups") {
17016
+ const groups = visibleRows.map((row) => ({ threadId: String(row.threadId) }));
17017
+ const last = groups.at(-1);
17018
+ return _mastra_core_storage.traceQueryResponseSchema.parse({
17019
+ groups,
17020
+ page: { next: rows.length > plan.limit && last ? _mastra_core_storage.encodeTraceQueryCursor(plan, {
17021
+ result: "groups",
17022
+ threadId: last.threadId
17023
+ }) : null }
17024
+ });
17025
+ }
17026
+ const traces = visibleRows.map((row) => ({
17027
+ traceId: String(row.traceId),
17028
+ rootSpanId: String(row.rootSpanId),
17029
+ threadId: row.threadId == null ? null : String(row.threadId),
17030
+ resourceId: row.resourceId == null ? null : String(row.resourceId),
17031
+ startedAt: asIsoTimestamp$1(row.startedAt),
17032
+ endedAt: asIsoTimestamp$1(row.endedAt),
17033
+ entityName: row.entityName == null ? null : String(row.entityName),
17034
+ entityType: row.entityType == null ? null : String(row.entityType),
17035
+ environment: row.environment == null ? null : String(row.environment),
17036
+ status: row.status
17037
+ }));
17038
+ const last = traces.at(-1);
17039
+ return _mastra_core_storage.traceQueryResponseSchema.parse({
17040
+ traces,
17041
+ page: { next: rows.length > plan.limit && last ? _mastra_core_storage.encodeTraceQueryCursor(plan, {
17042
+ result: "traces",
17043
+ sortValue: last[plan.orderBy.field],
17044
+ traceId: last.traceId
17045
+ }) : null }
17046
+ });
17047
+ }
17048
+ //#endregion
16742
17049
  //#region src/storage/domains/observability/v-next/traces.ts
16743
17050
  /**
16744
17051
  * Trace / branch list reads for the v-next Postgres observability domain.
@@ -17425,7 +17732,7 @@ async function dangerouslyClearTracing(client, schema) {
17425
17732
  * Use it through `MastraCompositeStore` with a dedicated Postgres connection.
17426
17733
  */
17427
17734
  function wrapError(op, error, details) {
17428
- if (error instanceof _mastra_core_error.MastraError) throw error;
17735
+ if (error instanceof _mastra_core_error.MastraError || error instanceof _mastra_core_storage.TraceQueryExecutionError) throw error;
17429
17736
  throw new _mastra_core_error.MastraError({
17430
17737
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", op, "FAILED"),
17431
17738
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -17438,6 +17745,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
17438
17745
  #schema;
17439
17746
  #partitioning;
17440
17747
  #discoveryConfig;
17748
+ #traceQueryTimeoutMs;
17441
17749
  #partitionMode;
17442
17750
  constructor(config) {
17443
17751
  super();
@@ -17446,6 +17754,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
17446
17754
  this.#schema = schemaName ?? "public";
17447
17755
  this.#partitioning = config.partitioning ?? {};
17448
17756
  this.#discoveryConfig = config.discovery ?? {};
17757
+ this.#traceQueryTimeoutMs = _mastra_core_storage.resolveTraceQueryTimeoutMs(config.traceQueryTimeoutMs);
17449
17758
  }
17450
17759
  /**
17451
17760
  * Build the discovery config used at each call site, with the framework
@@ -17597,11 +17906,16 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
17597
17906
  };
17598
17907
  }
17599
17908
  getFeatures() {
17600
- if (!deltaPollingFeatureEnabled()) return ["metrics", "logs"];
17909
+ if (!deltaPollingFeatureEnabled()) return [
17910
+ "metrics",
17911
+ "logs",
17912
+ "trace-query"
17913
+ ];
17601
17914
  return [
17602
17915
  "metrics",
17603
17916
  "logs",
17604
- "delta-polling"
17917
+ "delta-polling",
17918
+ "trace-query"
17605
17919
  ];
17606
17920
  }
17607
17921
  async #run(op, fn, details) {
@@ -17644,6 +17958,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
17644
17958
  async listTraces(args) {
17645
17959
  return this.#run("LIST_TRACES", () => listTraces(this.#client, this.#schema, args));
17646
17960
  }
17961
+ async queryTraces(plan) {
17962
+ return this.#run("QUERY_TRACES", () => queryTraces(this.#client, this.#schema, plan, this.#traceQueryTimeoutMs));
17963
+ }
17647
17964
  async listBranches(args) {
17648
17965
  return this.#run("LIST_BRANCHES", () => listBranches(this.#client, this.#schema, args));
17649
17966
  }
@@ -22843,7 +23160,8 @@ var PostgresStoreVNext = class extends PostgresStore {
22843
23160
  client: observabilityClient,
22844
23161
  schemaName: obsConfig.schemaName ?? config.schemaName,
22845
23162
  partitioning: obsConfig.partitioning,
22846
- discovery: obsConfig.discovery
23163
+ discovery: obsConfig.discovery,
23164
+ traceQueryTimeoutMs: obsConfig.traceQueryTimeoutMs
22847
23165
  });
22848
23166
  this.stores = {
22849
23167
  ...this.stores,