@mastra/pg 1.30.0-alpha.0 → 1.30.0-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.
package/dist/index.js CHANGED
@@ -18256,6 +18256,166 @@ async function queryThreads(client, schema, plan, timeoutMs) {
18256
18256
  }) : null }
18257
18257
  });
18258
18258
  }
18259
+ /** Compile a span-row filter with exactly the same rules as trace span predicates. */
18260
+ function compileSpanQueryPredicate(predicate, offset) {
18261
+ return compileScalarPredicate(predicate, SPAN_FIELDS, offset);
18262
+ }
18263
+ //#endregion
18264
+ //#region src/storage/domains/observability/v-next/span-query.ts
18265
+ const identityFields = [
18266
+ "organizationId",
18267
+ "resourceId",
18268
+ "traceId",
18269
+ "spanId"
18270
+ ];
18271
+ const fields = [
18272
+ ...identityFields,
18273
+ "parentSpanId",
18274
+ "name",
18275
+ "spanType",
18276
+ "entityType",
18277
+ "entityId",
18278
+ "entityName",
18279
+ "entityVersionId",
18280
+ "parentEntityVersionId",
18281
+ "rootEntityVersionId",
18282
+ "runId",
18283
+ "sessionId",
18284
+ "userId"
18285
+ ];
18286
+ const identities = identityFields.map((field) => `"${field}"`).join(", ");
18287
+ const identityOrder = identityFields.map((field) => `"${field}" COLLATE "C" ASC NULLS LAST`).join(", ");
18288
+ function selectedKeys(rows, values) {
18289
+ return rows.map((row) => `(${identityFields.map((field) => {
18290
+ values.push(row[field]);
18291
+ return `"${field}" ${field === "traceId" || field === "spanId" ? "=" : "IS NOT DISTINCT FROM"} $${values.length}::text`;
18292
+ }).join(" AND ")})`).join(" OR ");
18293
+ }
18294
+ function afterCursor(plan, values) {
18295
+ if (!plan.cursor) return "TRUE";
18296
+ const cursor = plan.cursor;
18297
+ values.push(cursor.sortValue);
18298
+ const timestamp = `$${values.length}::timestamptz`;
18299
+ const field = `s."${plan.orderBy.field}"`;
18300
+ const terms = [`${field} ${plan.orderBy.direction === "asc" ? ">" : "<"} ${timestamp}`];
18301
+ const equal = [`${field} = ${timestamp}`];
18302
+ for (const key of identityFields) {
18303
+ const ref = `s."${key}" COLLATE "C"`;
18304
+ const value = cursor[key];
18305
+ if (value === null) equal.push(`${ref} IS NULL`);
18306
+ else {
18307
+ values.push(value);
18308
+ const parameter = `$${values.length}::text COLLATE "C"`;
18309
+ terms.push(`(${equal.join(" AND ")} AND (${ref} > ${parameter} OR ${ref} IS NULL))`);
18310
+ equal.push(`${ref} = ${parameter}`);
18311
+ }
18312
+ }
18313
+ return `(${terms.join(" OR ")})`;
18314
+ }
18315
+ function compilePostgresSpanQuery(schema, plan) {
18316
+ const values = [plan.timeRange.from, plan.timeRange.to];
18317
+ const scope = [];
18318
+ if (plan.scope) {
18319
+ values.push(plan.scope.organizationId);
18320
+ scope.push(`"organizationId" = $${values.length}`);
18321
+ if (plan.scope.resourceId !== void 0) {
18322
+ values.push(plan.scope.resourceId);
18323
+ scope.push(`"resourceId" = $${values.length}`);
18324
+ }
18325
+ }
18326
+ const predicate = plan.where ? compileSpanQueryPredicate(plan.where, values.length + 1) : {
18327
+ sql: "TRUE",
18328
+ values: []
18329
+ };
18330
+ values.push(...predicate.values);
18331
+ const after = afterCursor(plan, values);
18332
+ values.push(plan.limit + 1);
18333
+ const table = qualifiedTable(schema, TABLE_SPAN_EVENTS);
18334
+ return {
18335
+ text: `WITH candidate_ids AS MATERIALIZED (
18336
+ SELECT DISTINCT ${identities} FROM ${table}
18337
+ WHERE "startedAt" >= $1 AND "startedAt" < $2 AND "endedAt" >= $1 AND NOT "isPending"
18338
+ ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
18339
+ ), current_spans AS MATERIALIZED (
18340
+ SELECT DISTINCT ON (${identities})
18341
+ ${fields.map((field) => `"${field}"`).join(", ")}, "startedAt", "endedAt", "cursorId"::text AS version,
18342
+ CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END AS model,
18343
+ CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END AS provider,
18344
+ (EXTRACT(EPOCH FROM ("endedAt" - "startedAt")) * 1000)::float8 AS "durationMs",
18345
+ CASE WHEN error IS NULL THEN 'success' ELSE 'error' END AS status,
18346
+ error IS NOT NULL AS "hasError"
18347
+ FROM ${table} e
18348
+ WHERE NOT "isPending" AND "endedAt" >= $1 AND (
18349
+ "organizationId" IS NULL, COALESCE("organizationId", ''), "resourceId" IS NULL, COALESCE("resourceId", ''), "traceId", "spanId"
18350
+ ) IN (
18351
+ SELECT "organizationId" IS NULL, COALESCE("organizationId", ''), "resourceId" IS NULL, COALESCE("resourceId", ''), "traceId", "spanId" FROM candidate_ids
18352
+ ) ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
18353
+ ORDER BY ${identities}, "endedAt" DESC, "cursorId" DESC
18354
+ ), candidates AS (
18355
+ SELECT *, CASE WHEN "hasError" THEN 'error' ELSE NULL END AS error FROM current_spans
18356
+ )
18357
+ SELECT ${[
18358
+ ...identityFields,
18359
+ "parentSpanId",
18360
+ "name",
18361
+ "spanType",
18362
+ "entityType",
18363
+ "entityId",
18364
+ "entityName",
18365
+ "model",
18366
+ "provider",
18367
+ "startedAt",
18368
+ "endedAt",
18369
+ "durationMs",
18370
+ "status",
18371
+ "version"
18372
+ ].map((field) => `s."${field}"`).join(", ")}
18373
+ FROM candidates s WHERE s."startedAt" >= $1 AND s."startedAt" < $2 AND (${predicate.sql}) AND ${after}
18374
+ ORDER BY s."${plan.orderBy.field}" ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"}, ${identityOrder}
18375
+ LIMIT $${values.length}`,
18376
+ values
18377
+ };
18378
+ }
18379
+ async function querySpans(client, schema, plan, timeoutMs) {
18380
+ const deadline = performance.now() + coreStorage.resolveTraceQueryTimeoutMs(timeoutMs);
18381
+ return runWithPostgresTraceQueryTimeout(client, timeoutMs, async (tx) => {
18382
+ const query = compilePostgresSpanQuery(schema, plan);
18383
+ const rows = await tx.any(query.text, query.values);
18384
+ const selected = rows.map(({ version: _version, startedAt, endedAt, ...row }) => ({
18385
+ ...row,
18386
+ startedAt: startedAt.toISOString(),
18387
+ endedAt: endedAt.toISOString()
18388
+ }));
18389
+ const page = rows.slice(0, plan.limit);
18390
+ if (!page.length) return coreStorage.buildSpanQueryResponse(plan, selected, [], []);
18391
+ const remaining = async () => {
18392
+ const ms = Math.floor(deadline - performance.now());
18393
+ if (ms <= 0) throw new coreStorage.TraceQueryExecutionError();
18394
+ await tx.query(`SELECT set_config('statement_timeout', $1, true)`, [`${ms}ms`]);
18395
+ };
18396
+ await remaining();
18397
+ const payloadValues = [];
18398
+ const payloadWhere = page.map((row) => {
18399
+ const key = selectedKeys([row], payloadValues);
18400
+ payloadValues.push(row.version);
18401
+ return `(${key} AND "cursorId" = $${payloadValues.length}::bigint)`;
18402
+ }).join(" OR ");
18403
+ const payloads = await tx.any(`SELECT ${identities},
18404
+ left(input::text, ${coreStorage.SPAN_QUERY_MAX_PREVIEW_CHARACTERS + 1}) AS "inputPreview", left(output::text, ${coreStorage.SPAN_QUERY_MAX_PREVIEW_CHARACTERS + 1}) AS "outputPreview"
18405
+ FROM ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} WHERE ${payloadWhere}`, payloadValues);
18406
+ await remaining();
18407
+ const metricValues = [];
18408
+ const metricWhere = selectedKeys(page, metricValues);
18409
+ const metrics = await tx.any(`SELECT ${identities}, name, "estimatedCost", "costUnit",
18410
+ "costMetadata" ->> 'allocation' AS allocation, COALESCE("costMetadata" ? 'error', false) AS "costError"
18411
+ FROM (SELECT DISTINCT ON (${identities}, "metricId") ${identities}, name, "estimatedCost", "costUnit", "costMetadata" FROM ${qualifiedTable(schema, TABLE_METRIC_EVENTS)}
18412
+ WHERE (${metricWhere}) AND name LIKE 'mastra_model_%'
18413
+ ORDER BY ${identities}, "metricId", "cursorId" DESC) m
18414
+ LIMIT ${coreStorage.SPAN_QUERY_MAX_COST_METRICS + 1}`, metricValues);
18415
+ if (metrics.length > coreStorage.SPAN_QUERY_MAX_COST_METRICS) throw new coreStorage.TraceQueryResourceLimitError();
18416
+ return coreStorage.buildSpanQueryResponse(plan, selected, payloads, metrics);
18417
+ }, { repeatableRead: true });
18418
+ }
18259
18419
  //#endregion
18260
18420
  //#region src/storage/domains/observability/v-next/trace-aggregate.ts
18261
18421
  /** Trace-root column expressions (alias `r`) for each groupable dimension; same values `where` sees. */
@@ -19151,6 +19311,7 @@ async function dangerouslyClearTracing(client, schema) {
19151
19311
  * application database — observability writes will degrade app performance.
19152
19312
  * Use it through `MastraCompositeStore` with a dedicated Postgres connection.
19153
19313
  */
19314
+ const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? ["span-query"] : [];
19154
19315
  function wrapError(op, error, details) {
19155
19316
  if (error instanceof MastraError || error instanceof coreStorage.TraceQueryCursorError || error instanceof coreStorage.TraceQueryExecutionError || error instanceof coreStorage.TraceQueryResourceLimitError) throw error;
19156
19317
  throw new MastraError({
@@ -19340,6 +19501,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
19340
19501
  "metric-discovery",
19341
19502
  "trace-query",
19342
19503
  "trace-aggregate",
19504
+ ...spanQueryFeatures,
19343
19505
  "trace-query-root-duration",
19344
19506
  "trace-query-discovery",
19345
19507
  "thread-query",
@@ -19359,6 +19521,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
19359
19521
  "delta-polling",
19360
19522
  "trace-query",
19361
19523
  "trace-aggregate",
19524
+ ...spanQueryFeatures,
19362
19525
  "trace-query-root-duration",
19363
19526
  "trace-query-discovery",
19364
19527
  "thread-query",
@@ -19407,6 +19570,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
19407
19570
  async listTraces(args) {
19408
19571
  return this.#run("LIST_TRACES", () => listTraces(this.#readClient, this.#schema, args));
19409
19572
  }
19573
+ async querySpans(plan) {
19574
+ return this.#run("QUERY_SPANS", () => querySpans(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
19575
+ }
19410
19576
  async queryTraces(plan) {
19411
19577
  return this.#run("QUERY_TRACES", () => queryTraces(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
19412
19578
  }
@@ -20424,9 +20590,9 @@ var SchedulesPG = class SchedulesPG extends SchedulesStorage {
20424
20590
  if (!updated) throw new Error(`Schedule ${id} not found`);
20425
20591
  return updated;
20426
20592
  }
20427
- async updateScheduleNextFire(id, expectedNextFireAt, newNextFireAt, lastFireAt, lastRunId) {
20593
+ async updateScheduleNextFire(id, expectedNextFireAt, newNextFireAt, lastFireAt, lastRunId, newStatus) {
20428
20594
  return ((await this.#client.query(`UPDATE ${this.#table(TABLE_SCHEDULES)}
20429
- SET next_fire_at = $1, last_fire_at = $2, last_run_id = $3, updated_at = $4
20595
+ SET next_fire_at = $1, last_fire_at = $2, last_run_id = $3, updated_at = $4, status = COALESCE($8, status)
20430
20596
  WHERE id = $5 AND next_fire_at = $6 AND status = $7`, [
20431
20597
  newNextFireAt,
20432
20598
  lastFireAt,
@@ -20434,7 +20600,8 @@ var SchedulesPG = class SchedulesPG extends SchedulesStorage {
20434
20600
  Date.now(),
20435
20601
  id,
20436
20602
  expectedNextFireAt,
20437
- "active"
20603
+ "active",
20604
+ newStatus ?? null
20438
20605
  ])).rowCount ?? 0) > 0;
20439
20606
  }
20440
20607
  async deleteSchedule(id) {