@mastra/pg 1.31.2-alpha.0 → 1.31.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.
Files changed (26) hide show
  1. package/dist/docs/SKILL.md +1 -1
  2. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  3. package/dist/docs/references/reference-storage-retention.md +15 -12
  4. package/dist/index.cjs +406 -146
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.js +407 -147
  7. package/dist/index.js.map +1 -1
  8. package/dist/storage/db/sanitize-json.d.ts +2 -0
  9. package/dist/storage/db/sanitize-json.d.ts.map +1 -1
  10. package/dist/storage/domains/observability/v-next/ddl.d.ts.map +1 -1
  11. package/dist/storage/domains/observability/v-next/helpers.d.ts +7 -0
  12. package/dist/storage/domains/observability/v-next/helpers.d.ts.map +1 -1
  13. package/dist/storage/domains/observability/v-next/index.d.ts +2 -1
  14. package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
  15. package/dist/storage/domains/observability/v-next/preview-compat.d.ts +47 -0
  16. package/dist/storage/domains/observability/v-next/preview-compat.d.ts.map +1 -0
  17. package/dist/storage/domains/observability/v-next/signal-schema.d.ts +12 -0
  18. package/dist/storage/domains/observability/v-next/signal-schema.d.ts.map +1 -1
  19. package/dist/storage/domains/observability/v-next/span-query.d.ts +2 -0
  20. package/dist/storage/domains/observability/v-next/span-query.d.ts.map +1 -1
  21. package/dist/storage/domains/observability/v-next/sql.d.ts +11 -0
  22. package/dist/storage/domains/observability/v-next/sql.d.ts.map +1 -1
  23. package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
  24. package/dist/storage/domains/observability/v-next/traces.d.ts +3 -1
  25. package/dist/storage/domains/observability/v-next/traces.d.ts.map +1 -1
  26. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
2
2
  import * as coreStorage from "@mastra/core/storage";
3
- import { AgentsStorage, BRANCH_SPAN_TYPES, BackgroundTasksStorage, BlobStore, ChannelsStorage, DATASETS_SCHEMA, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, DatasetsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, EntityType, ExperimentsStorage, FactoryStorage, FavoritesStorage, KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA, KnowledgeConflictError, KnowledgeNotFoundError, KnowledgeStorage, MCPClientsStorage, MCPServersStorage, METRIC_DISTINCT_COLUMNS, MastraCompositeStore, MemoryStorage, NotificationsStorage, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, ObservabilityStorage, PromptBlocksStorage, RUN_FENCING_TABLE_SCHEMAS, RunFenceConflictError, SchedulesStorage, ScorerDefinitionsStorage, ScoresStorage, SkillsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_CONFIGS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_KNOWLEDGE_ACTIVITY, TABLE_KNOWLEDGE_MENTIONS, TABLE_KNOWLEDGE_NODES, TABLE_KNOWLEDGE_RECORDS, TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MEMORY_RUN_FENCES, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_RUN_OWNERS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, ThreadStateStorage, ToolProviderConnectionsStorage, TraceStatus, UniqueViolationError, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, calculatePagination, canonicalizeKnowledgeScope, createKnowledgeUlid, createStorageErrorId, createVectorErrorId, ensureDate, executeRetentionPrune, getDefaultValue, getSqlType, hasErrorCode, isKnowledgeScopeVisible, isRunFenceConflictError, knowledgeScopeKey, knowledgeSemanticDocumentId, knowledgeSemanticIdempotencyKey, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, matchesExpectedWorkflowStatus, matchesRunFence, mergeWorkflowStepResult, normalizePerPage, normalizeScheduleTarget, parseDuration, parseKnowledgeNodeCursor, parseKnowledgeWikilinks, resolveListOrderBy, resolveRetentionTargets, resolveRunFence, retentionCutoffMs, runRetentionBatches, safelyParseJSON, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
3
+ import { AgentsStorage, BRANCH_SPAN_TYPES, BackgroundTasksStorage, BlobStore, ChannelsStorage, DATASETS_SCHEMA, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, DatasetsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, EntityType, ExperimentsStorage, FactoryStorage, FavoritesStorage, KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA, KnowledgeConflictError, KnowledgeNotFoundError, KnowledgeStorage, MCPClientsStorage, MCPServersStorage, METRIC_DISTINCT_COLUMNS, MastraCompositeStore, MemoryStorage, NotificationsStorage, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, ObservabilityStorage, PromptBlocksStorage, RUN_FENCING_TABLE_SCHEMAS, RunFenceConflictError, SchedulesStorage, ScorerDefinitionsStorage, ScoresStorage, SkillsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_CONFIGS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_KNOWLEDGE_ACTIVITY, TABLE_KNOWLEDGE_MENTIONS, TABLE_KNOWLEDGE_NODES, TABLE_KNOWLEDGE_RECORDS, TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MEMORY_RUN_FENCES, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_RUN_OWNERS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, ThreadStateStorage, ToolProviderConnectionsStorage, TraceStatus, UniqueViolationError, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, calculatePagination, canonicalizeKnowledgeScope, computeTraceStatus, createKnowledgeUlid, createStorageErrorId, createVectorErrorId, ensureDate, executeRetentionPrune, getDefaultValue, getSqlType, hasErrorCode, isKnowledgeScopeVisible, isRunFenceConflictError, knowledgeScopeKey, knowledgeSemanticDocumentId, knowledgeSemanticIdempotencyKey, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, matchesExpectedWorkflowStatus, matchesRunFence, mergeWorkflowStepResult, normalizePerPage, normalizeScheduleTarget, parseDuration, parseKnowledgeNodeCursor, parseKnowledgeWikilinks, resolveListOrderBy, resolveRetentionTargets, resolveRunFence, retentionCutoffMs, runRetentionBatches, safelyParseJSON, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
4
4
  import { parseFieldKey, parseSqlIdentifier } from "@mastra/core/utils";
5
5
  import { MastraVector, validateTopK, validateUpsertInput } from "@mastra/core/vector";
6
6
  import { Mutex } from "async-mutex";
@@ -2448,6 +2448,10 @@ function repairJson(value) {
2448
2448
  }
2449
2449
  return value;
2450
2450
  }
2451
+ /** Repair text for a PostgreSQL `text` column, which rejects NUL characters. */
2452
+ function toPgText(value) {
2453
+ return repair(value);
2454
+ }
2451
2455
  /** Serialize JSON values without emitting Unicode sequences PostgreSQL rejects. */
2452
2456
  function toPgJson(value) {
2453
2457
  const json = JSON.stringify(value);
@@ -16051,6 +16055,21 @@ const SPAN_EVENT_COLUMNS = [
16051
16055
  name: "requestContext",
16052
16056
  type: "jsonb",
16053
16057
  nullable: true
16058
+ },
16059
+ {
16060
+ name: "inputPreview",
16061
+ type: "text",
16062
+ nullable: true
16063
+ },
16064
+ {
16065
+ name: "outputPreview",
16066
+ type: "text",
16067
+ nullable: true
16068
+ },
16069
+ {
16070
+ name: "errorPreview",
16071
+ type: "text",
16072
+ nullable: true
16054
16073
  }
16055
16074
  ];
16056
16075
  const METRIC_EVENT_COLUMNS = [
@@ -16706,15 +16725,27 @@ function allTableDDL(schema, mode) {
16706
16725
  * default does not rewrite the table, and the ALTER cascades to partitions.
16707
16726
  */
16708
16727
  function additiveColumns(schema) {
16709
- return [{
16710
- table: TABLE_SPAN_EVENTS,
16711
- column: "isPending",
16712
- ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "isPending" boolean NOT NULL DEFAULT false`
16713
- }, {
16714
- table: TABLE_FEEDBACK_EVENTS,
16715
- column: "reviewStatus",
16716
- ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)} ADD COLUMN IF NOT EXISTS "reviewStatus" text NOT NULL DEFAULT 'needs-review'`
16717
- }];
16728
+ return [
16729
+ {
16730
+ table: TABLE_SPAN_EVENTS,
16731
+ column: "isPending",
16732
+ ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "isPending" boolean NOT NULL DEFAULT false`
16733
+ },
16734
+ ...[
16735
+ "inputPreview",
16736
+ "outputPreview",
16737
+ "errorPreview"
16738
+ ].map((column) => ({
16739
+ table: TABLE_SPAN_EVENTS,
16740
+ column,
16741
+ ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "${column}" text`
16742
+ })),
16743
+ {
16744
+ table: TABLE_FEEDBACK_EVENTS,
16745
+ column: "reviewStatus",
16746
+ ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)} ADD COLUMN IF NOT EXISTS "reviewStatus" text NOT NULL DEFAULT 'needs-review'`
16747
+ }
16748
+ ];
16718
16749
  }
16719
16750
  /** Existence probe for an additive column. Params: schema, table, column. */
16720
16751
  const columnExistsSQL = `SELECT 1 FROM information_schema.columns
@@ -17036,6 +17067,46 @@ function whereOrEmpty(acc) {
17036
17067
  return acc.conditions.length ? `WHERE ${acc.conditions.join(" AND ")}` : "";
17037
17068
  }
17038
17069
  //#endregion
17070
+ //#region src/storage/domains/observability/v-next/preview-compat.ts
17071
+ const generator = coreStorage.buildStoredPreview;
17072
+ const canGeneratePreviews = typeof generator === "function";
17073
+ /** SQL detection only; preview formatting stays in TypeScript. Empty strings are intentional. */
17074
+ function missingPreviewSql(column) {
17075
+ return canGeneratePreviews ? `${column} IS NULL` : "FALSE";
17076
+ }
17077
+ function needsPreview(value) {
17078
+ return canGeneratePreviews && value == null;
17079
+ }
17080
+ function storedPreview(preview, value, serialized) {
17081
+ return canGeneratePreviews ? generator(preview, value, serialized) : preview ?? null;
17082
+ }
17083
+ function readPreview(preview, payload, jsonEncoded = false) {
17084
+ const supplied = preview;
17085
+ if (!canGeneratePreviews) return supplied ?? null;
17086
+ if (supplied != null) return supplied.trim() || null;
17087
+ let value = payload;
17088
+ if (needsPreview(preview) && jsonEncoded && typeof payload === "string") try {
17089
+ value = JSON.parse(payload);
17090
+ } catch {
17091
+ value = null;
17092
+ }
17093
+ return storedPreview(supplied, value, jsonEncoded && typeof payload === "string" ? payload : void 0) || null;
17094
+ }
17095
+ /** Older response builders may shorten previews or turn empty strings into null. */
17096
+ function buildSpanQueryResponse(...args) {
17097
+ if (canGeneratePreviews) return coreStorage.buildSpanQueryResponse(...args);
17098
+ const [plan, selected, payloads, metrics] = args;
17099
+ const response = coreStorage.buildSpanQueryResponse(plan, selected, [], metrics);
17100
+ const byIdentity = new Map(payloads.map((payload) => [coreStorage.spanQueryIdentityKey(payload), payload]));
17101
+ for (const span of response.spans) {
17102
+ const payload = byIdentity.get(coreStorage.spanQueryIdentityKey(span));
17103
+ span.inputPreview = payload?.inputPreview ?? null;
17104
+ span.outputPreview = payload?.outputPreview ?? null;
17105
+ span.errorPreview = payload?.errorPreview ?? null;
17106
+ }
17107
+ return response;
17108
+ }
17109
+ //#endregion
17039
17110
  //#region src/storage/domains/observability/v-next/review-status.ts
17040
17111
  const FEEDBACK_REVIEW_STATUSES = ["needs-review", "reviewed"];
17041
17112
  function isFeedbackReviewStatus(value) {
@@ -17061,6 +17132,137 @@ function parseUpdateFeedbackReviewStatusArgs(args) {
17061
17132
  };
17062
17133
  }
17063
17134
  //#endregion
17135
+ //#region src/storage/domains/observability/v-next/sql.ts
17136
+ /**
17137
+ * SQL helpers for the v-next Postgres observability domain.
17138
+ *
17139
+ * Provides a multi-row INSERT builder with a configurable conflict clause
17140
+ * (`ON CONFLICT DO NOTHING` by default, for retry idempotency), and explicit
17141
+ * jsonb / text[] casts so the pg driver doesn't have to guess column types.
17142
+ */
17143
+ /**
17144
+ * Encode a JS value for a `$N::jsonb` cast. Always `JSON.stringify` so a
17145
+ * plain string like `"hello"` becomes `"hello"` (a valid JSON scalar) and
17146
+ * not the bare word `hello`, which Postgres rejects when cast to jsonb.
17147
+ *
17148
+ * The result is sanitized because Postgres rejects some sequences that are
17149
+ * legal in JSON: NUL (`\u0000`) fails with 22P05 and unpaired UTF-16
17150
+ * surrogates fail with 22P02. Inserts here are batched into a single
17151
+ * multi-row statement, so one bad value would otherwise reject the whole
17152
+ * batch of observability events.
17153
+ */
17154
+ function encodeJsonb(value) {
17155
+ if (value === null || value === void 0) return null;
17156
+ if (value instanceof EncodedJsonb) return value.json;
17157
+ return toPgJson(value);
17158
+ }
17159
+ /**
17160
+ * A jsonb value the caller already serialized with `toPgJson`, so `buildInsert`
17161
+ * sends it as is instead of serializing it again. Span writes serialize their
17162
+ * payloads up front because the stored previews are built from that same text.
17163
+ */
17164
+ var EncodedJsonb = class {
17165
+ json;
17166
+ constructor(json) {
17167
+ this.json = json;
17168
+ }
17169
+ };
17170
+ /** Serialize a value for a jsonb column once, for callers that also need the text. */
17171
+ function encodeJsonbValue(value) {
17172
+ if (value === null || value === void 0) return null;
17173
+ const json = toPgJson(value);
17174
+ return json === void 0 ? null : new EncodedJsonb(json);
17175
+ }
17176
+ /**
17177
+ * Build a multi-row INSERT with explicit column types and ON CONFLICT DO NOTHING.
17178
+ *
17179
+ * @param schema Schema name.
17180
+ * @param table Table name.
17181
+ * @param records Array of records (each is a column-name → value object).
17182
+ * All records must have identical key sets.
17183
+ * @returns { text, values } ready to pass to `client.query`.
17184
+ */
17185
+ function buildInsert(schema, table, records, onConflict = "ON CONFLICT DO NOTHING") {
17186
+ if (records.length === 0) return null;
17187
+ const columns = Object.keys(records[0]).map((c) => parseSqlIdentifier(c, "column name"));
17188
+ const quotedColumns = columns.map((c) => `"${c}"`).join(", ");
17189
+ const values = [];
17190
+ const rowPlaceholders = [];
17191
+ let p = 1;
17192
+ for (const record of records) {
17193
+ const placeholders = columns.map((col) => {
17194
+ const raw = record[col];
17195
+ if (JSONB_COLUMNS.has(col)) {
17196
+ values.push(encodeJsonb(raw));
17197
+ return `$${p++}::jsonb`;
17198
+ }
17199
+ if (TEXT_ARRAY_COLUMNS.has(col)) {
17200
+ values.push(Array.isArray(raw) ? raw : []);
17201
+ return `$${p++}::text[]`;
17202
+ }
17203
+ values.push(raw === void 0 ? null : raw);
17204
+ return `$${p++}`;
17205
+ });
17206
+ rowPlaceholders.push(`(${placeholders.join(", ")})`);
17207
+ }
17208
+ return {
17209
+ text: `INSERT INTO ${qualifiedTable(schema, table)} (${quotedColumns})
17210
+ VALUES ${rowPlaceholders.join(", ")}
17211
+ ${onConflict}`,
17212
+ values
17213
+ };
17214
+ }
17215
+ /** Primary key of the span table; also the conflict target for span upserts. */
17216
+ const SPAN_KEY_COLUMNS = [
17217
+ "traceId",
17218
+ "spanId",
17219
+ "endedAt"
17220
+ ];
17221
+ /**
17222
+ * Conflict clause for span writes.
17223
+ *
17224
+ * A span is written twice under the event-sourced strategy: once when it starts
17225
+ * (`isPending = true`, `endedAt` synthesized from `startedAt`) and once when it
17226
+ * ends. Those two rows normally differ in `endedAt` and so coexist under the
17227
+ * primary key. A zero-duration span is the exception — its real `endedAt`
17228
+ * equals its `startedAt`, so the end row collides with the pending row. Letting
17229
+ * `DO NOTHING` win there would strand the span as permanently running, so the
17230
+ * end row overwrites the pending one instead.
17231
+ *
17232
+ * The `WHERE` guard keeps the clause a no-op for every other conflict, which
17233
+ * preserves insert-only retry idempotency: a replayed end row cannot clobber
17234
+ * the row it duplicates, and a late-arriving start row cannot revert an ended
17235
+ * span to pending.
17236
+ */
17237
+ function spanConflictClause(columns) {
17238
+ return `ON CONFLICT ("traceId", "spanId", "endedAt") DO UPDATE SET ${columns.filter((column) => !SPAN_KEY_COLUMNS.includes(column)).map((column) => `"${column}" = EXCLUDED."${column}"`).join(", ")}
17239
+ WHERE ${TABLE_SPAN_EVENTS}."isPending" AND NOT EXCLUDED."isPending"`;
17240
+ }
17241
+ /**
17242
+ * Ordering that picks the winning row for a span under the event-sourced write
17243
+ * model. An ended row always beats a pending row for the same span; among rows
17244
+ * of the same kind the latest write wins, with `cursorId` breaking ties between
17245
+ * rows that share a timestamp. Combine with `DISTINCT ON` (or `LIMIT 1`) after
17246
+ * the grouping key to collapse a span's rows down to one.
17247
+ */
17248
+ const SPAN_COLLAPSE_ORDER = "\"isPending\" ASC, \"endedAt\" DESC, \"cursorId\" DESC";
17249
+ /**
17250
+ * Standard SELECT column list for tracing tables. The select projects every
17251
+ * column the row→record converters expect.
17252
+ */
17253
+ const SPAN_SELECT_COLUMNS = `
17254
+ ${buildSelectColumns(SPAN_EVENT_COLUMNS)}`;
17255
+ const SPAN_LIGHT_SELECT_COLUMNS = `
17256
+ ${buildNamedSelectColumns(SPAN_LIGHT_SELECT_COLUMN_NAMES)}`;
17257
+ const METRIC_SELECT_COLUMNS = `
17258
+ ${buildSelectColumns(METRIC_EVENT_COLUMNS)}`;
17259
+ const LOG_SELECT_COLUMNS = `
17260
+ ${buildSelectColumns(LOG_EVENT_COLUMNS)}`;
17261
+ const SCORE_SELECT_COLUMNS = `
17262
+ ${buildSelectColumns(SCORE_EVENT_COLUMNS)}`;
17263
+ const FEEDBACK_SELECT_COLUMNS = `
17264
+ ${buildSelectColumns(FEEDBACK_EVENT_COLUMNS)}`;
17265
+ //#endregion
17064
17266
  //#region src/storage/domains/observability/v-next/helpers.ts
17065
17267
  const PROMOTED_KEYS = /* @__PURE__ */ new Set([
17066
17268
  "experimentId",
@@ -17210,10 +17412,26 @@ function commonContextToRow(record) {
17210
17412
  serviceName: record.serviceName ?? null
17211
17413
  };
17212
17414
  }
17415
+ /**
17416
+ * Serialize a span payload for its jsonb column and build its stored preview from the same value.
17417
+ * Nonblank upstream previews win (trimmed and capped like generated ones). The preview goes into a `text` column, which
17418
+ * rejects NUL, so it gets the same repair `toPgJson` applies to the payload.
17419
+ */
17420
+ function spanPayload(value, upstream) {
17421
+ const encoded = encodeJsonbValue(value);
17422
+ const preview = storedPreview(upstream, value, encoded?.json);
17423
+ return {
17424
+ encoded,
17425
+ preview: preview === null ? null : toPgText(preview)
17426
+ };
17427
+ }
17213
17428
  function spanRecordToRow(span) {
17214
17429
  const isPending = !span.isEvent && span.endedAt == null;
17215
17430
  const endedAt = span.isEvent ? span.startedAt : span.endedAt ?? span.startedAt;
17216
17431
  const metadata = span.metadata ?? null;
17432
+ const input = spanPayload(span.input, span.inputPreview);
17433
+ const output = spanPayload(span.output, span.outputPreview);
17434
+ const error = spanPayload(span.error, span.errorPreview);
17217
17435
  return {
17218
17436
  ...commonContextToRow(span),
17219
17437
  traceId: span.traceId,
@@ -17231,10 +17449,13 @@ function spanRecordToRow(span) {
17231
17449
  scope: jsonField(span.scope),
17232
17450
  attributes: jsonField(span.attributes),
17233
17451
  links: jsonField(span.links),
17234
- input: jsonField(span.input),
17235
- output: jsonField(span.output),
17236
- error: jsonField(span.error),
17237
- requestContext: jsonField(span.requestContext)
17452
+ input: input.encoded,
17453
+ output: output.encoded,
17454
+ error: error.encoded,
17455
+ requestContext: jsonField(span.requestContext),
17456
+ inputPreview: input.preview,
17457
+ outputPreview: output.preview,
17458
+ errorPreview: error.preview
17238
17459
  };
17239
17460
  }
17240
17461
  /**
@@ -17301,6 +17522,32 @@ function rowToLightSpanRecord(row) {
17301
17522
  updatedAt: null
17302
17523
  };
17303
17524
  }
17525
+ /** A stored preview as lists return it: `''` (nothing to preview) and NULL become null. */
17526
+ function listPreview(value) {
17527
+ return readPreview(value);
17528
+ }
17529
+ /**
17530
+ * Build a trace-list {@link LightSpanRecord} from a row that projected the light trace-list columns
17531
+ * (see `TRACE_LIGHT_SELECT_COLUMNS` in traces.ts): the timeline fields plus the memory context,
17532
+ * metadata and the stored previews. Callers fill in previews for rows written before they were
17533
+ * stored first.
17534
+ */
17535
+ function rowToLightTraceRecord(row) {
17536
+ const record = rowToLightSpanRecord(row);
17537
+ return {
17538
+ ...record,
17539
+ status: computeTraceStatus({
17540
+ error: record.error,
17541
+ endedAt: record.endedAt
17542
+ }),
17543
+ threadId: row.threadId == null ? null : String(row.threadId),
17544
+ resourceId: row.resourceId == null ? null : String(row.resourceId),
17545
+ metadata: parsedJson(row.metadataRaw) ?? void 0,
17546
+ inputPreview: listPreview(row.inputPreview),
17547
+ outputPreview: listPreview(row.outputPreview),
17548
+ errorPreview: listPreview(row.errorPreview)
17549
+ };
17550
+ }
17304
17551
  function logRecordToRow(log) {
17305
17552
  return {
17306
17553
  ...commonContextToRow(log),
@@ -17816,119 +18063,6 @@ const COMPLEX_GROUP_BY_EXCLUDED = /* @__PURE__ */ new Set([
17816
18063
  "requestContext"
17817
18064
  ]);
17818
18065
  //#endregion
17819
- //#region src/storage/domains/observability/v-next/sql.ts
17820
- /**
17821
- * SQL helpers for the v-next Postgres observability domain.
17822
- *
17823
- * Provides a multi-row INSERT builder with a configurable conflict clause
17824
- * (`ON CONFLICT DO NOTHING` by default, for retry idempotency), and explicit
17825
- * jsonb / text[] casts so the pg driver doesn't have to guess column types.
17826
- */
17827
- /**
17828
- * Encode a JS value for a `$N::jsonb` cast. Always `JSON.stringify` so a
17829
- * plain string like `"hello"` becomes `"hello"` (a valid JSON scalar) and
17830
- * not the bare word `hello`, which Postgres rejects when cast to jsonb.
17831
- *
17832
- * The result is sanitized because Postgres rejects some sequences that are
17833
- * legal in JSON: NUL (`\u0000`) fails with 22P05 and unpaired UTF-16
17834
- * surrogates fail with 22P02. Inserts here are batched into a single
17835
- * multi-row statement, so one bad value would otherwise reject the whole
17836
- * batch of observability events.
17837
- */
17838
- function encodeJsonb(value) {
17839
- if (value === null || value === void 0) return null;
17840
- return toPgJson(value);
17841
- }
17842
- /**
17843
- * Build a multi-row INSERT with explicit column types and ON CONFLICT DO NOTHING.
17844
- *
17845
- * @param schema Schema name.
17846
- * @param table Table name.
17847
- * @param records Array of records (each is a column-name → value object).
17848
- * All records must have identical key sets.
17849
- * @returns { text, values } ready to pass to `client.query`.
17850
- */
17851
- function buildInsert(schema, table, records, onConflict = "ON CONFLICT DO NOTHING") {
17852
- if (records.length === 0) return null;
17853
- const columns = Object.keys(records[0]).map((c) => parseSqlIdentifier(c, "column name"));
17854
- const quotedColumns = columns.map((c) => `"${c}"`).join(", ");
17855
- const values = [];
17856
- const rowPlaceholders = [];
17857
- let p = 1;
17858
- for (const record of records) {
17859
- const placeholders = columns.map((col) => {
17860
- const raw = record[col];
17861
- if (JSONB_COLUMNS.has(col)) {
17862
- values.push(encodeJsonb(raw));
17863
- return `$${p++}::jsonb`;
17864
- }
17865
- if (TEXT_ARRAY_COLUMNS.has(col)) {
17866
- values.push(Array.isArray(raw) ? raw : []);
17867
- return `$${p++}::text[]`;
17868
- }
17869
- values.push(raw === void 0 ? null : raw);
17870
- return `$${p++}`;
17871
- });
17872
- rowPlaceholders.push(`(${placeholders.join(", ")})`);
17873
- }
17874
- return {
17875
- text: `INSERT INTO ${qualifiedTable(schema, table)} (${quotedColumns})
17876
- VALUES ${rowPlaceholders.join(", ")}
17877
- ${onConflict}`,
17878
- values
17879
- };
17880
- }
17881
- /** Primary key of the span table; also the conflict target for span upserts. */
17882
- const SPAN_KEY_COLUMNS = [
17883
- "traceId",
17884
- "spanId",
17885
- "endedAt"
17886
- ];
17887
- /**
17888
- * Conflict clause for span writes.
17889
- *
17890
- * A span is written twice under the event-sourced strategy: once when it starts
17891
- * (`isPending = true`, `endedAt` synthesized from `startedAt`) and once when it
17892
- * ends. Those two rows normally differ in `endedAt` and so coexist under the
17893
- * primary key. A zero-duration span is the exception — its real `endedAt`
17894
- * equals its `startedAt`, so the end row collides with the pending row. Letting
17895
- * `DO NOTHING` win there would strand the span as permanently running, so the
17896
- * end row overwrites the pending one instead.
17897
- *
17898
- * The `WHERE` guard keeps the clause a no-op for every other conflict, which
17899
- * preserves insert-only retry idempotency: a replayed end row cannot clobber
17900
- * the row it duplicates, and a late-arriving start row cannot revert an ended
17901
- * span to pending.
17902
- */
17903
- function spanConflictClause(columns) {
17904
- return `ON CONFLICT ("traceId", "spanId", "endedAt") DO UPDATE SET ${columns.filter((column) => !SPAN_KEY_COLUMNS.includes(column)).map((column) => `"${column}" = EXCLUDED."${column}"`).join(", ")}
17905
- WHERE ${TABLE_SPAN_EVENTS}."isPending" AND NOT EXCLUDED."isPending"`;
17906
- }
17907
- /**
17908
- * Ordering that picks the winning row for a span under the event-sourced write
17909
- * model. An ended row always beats a pending row for the same span; among rows
17910
- * of the same kind the latest write wins, with `cursorId` breaking ties between
17911
- * rows that share a timestamp. Combine with `DISTINCT ON` (or `LIMIT 1`) after
17912
- * the grouping key to collapse a span's rows down to one.
17913
- */
17914
- const SPAN_COLLAPSE_ORDER = "\"isPending\" ASC, \"endedAt\" DESC, \"cursorId\" DESC";
17915
- /**
17916
- * Standard SELECT column list for tracing tables. The select projects every
17917
- * column the row→record converters expect.
17918
- */
17919
- const SPAN_SELECT_COLUMNS = `
17920
- ${buildSelectColumns(SPAN_EVENT_COLUMNS)}`;
17921
- const SPAN_LIGHT_SELECT_COLUMNS = `
17922
- ${buildNamedSelectColumns(SPAN_LIGHT_SELECT_COLUMN_NAMES)}`;
17923
- const METRIC_SELECT_COLUMNS = `
17924
- ${buildSelectColumns(METRIC_EVENT_COLUMNS)}`;
17925
- const LOG_SELECT_COLUMNS = `
17926
- ${buildSelectColumns(LOG_EVENT_COLUMNS)}`;
17927
- const SCORE_SELECT_COLUMNS = `
17928
- ${buildSelectColumns(SCORE_EVENT_COLUMNS)}`;
17929
- const FEEDBACK_SELECT_COLUMNS = `
17930
- ${buildSelectColumns(FEEDBACK_EVENT_COLUMNS)}`;
17931
- //#endregion
17932
18066
  //#region src/storage/domains/observability/v-next/feedback.ts
17933
18067
  /**
17934
18068
  * Feedback operations for the v-next Postgres observability domain.
@@ -19087,7 +19221,10 @@ const TRACE_SELECT = `
19087
19221
  r."entityId" AS "entityId",
19088
19222
  r."parentSpanId" AS "parentSpanId",
19089
19223
  r."metadataRaw" AS "metadata",
19090
- r."input" AS "input",
19224
+ r."inputPreview" AS "inputPreview",
19225
+ r."outputPreview" AS "outputPreview",
19226
+ r."errorPreview" AS "errorPreview",
19227
+ r."cursorId" AS "cursorId",
19091
19228
  r."threadId" AS "threadId",
19092
19229
  r."resourceId" AS "resourceId",
19093
19230
  r."startedAt" AS "startedAt",
@@ -19545,7 +19682,7 @@ function compilePostgresTraceQuery(schema, plan, mode = "data", safeHorizon) {
19545
19682
  safeHorizon
19546
19683
  };
19547
19684
  }
19548
- const { ctes, values } = compilePostgresTraceCandidates(schema, plan, `${TRACE_SELECT}${plan.paginationMode === "delta" ? ", r.\"xactId\", r.\"cursorId\"" : ""}`, deltaWindow);
19685
+ const { ctes, values } = compilePostgresTraceCandidates(schema, plan, `${TRACE_SELECT}${plan.paginationMode === "delta" ? ", r.\"xactId\"" : ""}`, deltaWindow);
19549
19686
  const candidates = `WITH ${ctes.join(",\n")}`;
19550
19687
  if (plan.paginationMode === "page" && mode === "count") return {
19551
19688
  text: `${candidates}
@@ -19787,6 +19924,34 @@ async function setRemainingTimeout(transaction, deadline) {
19787
19924
  if (remainingTimeoutMs <= 0) throw new coreStorage.TraceQueryExecutionError();
19788
19925
  await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${remainingTimeoutMs}ms`]);
19789
19926
  }
19927
+ const PREVIEW_PAYLOADS$2 = [
19928
+ ["inputPreview", "input"],
19929
+ ["outputPreview", "output"],
19930
+ ["errorPreview", "error"]
19931
+ ];
19932
+ /**
19933
+ * Fill the previews of root rows written before the preview columns existed (stored preview NULL)
19934
+ * by reading just those rows' payloads, and only the payloads whose preview is missing. Rows are
19935
+ * keyed by `cursorId`, which identifies the exact root row the list picked.
19936
+ *
19937
+ * jsonb comes back decoded, so the preview is built with `buildPreview(value)`. jsonb also
19938
+ * normalizes object key order, so a compact-JSON fallback preview can differ from the one the
19939
+ * writer would have stored from the original value. That is acceptable for these old rows.
19940
+ */
19941
+ async function fillMissingTracePreviews(transaction, schema, rows, deadline) {
19942
+ const missing = rows.filter((row) => PREVIEW_PAYLOADS$2.some(([preview]) => needsPreview(row[preview])));
19943
+ if (!missing.length) return;
19944
+ await setRemainingTimeout(transaction, deadline);
19945
+ const payloads = await transaction.any(`SELECT e."cursorId"::text AS "cursorId",
19946
+ ${PREVIEW_PAYLOADS$2.map(([preview, payload]) => `${missing.some((row) => needsPreview(row[preview])) ? `e."${payload}"` : "NULL"} AS "${payload}"`).join(",\n ")}
19947
+ FROM ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} e
19948
+ WHERE (e."traceId", e."cursorId") IN (SELECT * FROM unnest($1::text[], $2::bigint[]))`, [missing.map((row) => String(row.traceId)), missing.map((row) => String(row.cursorId))]);
19949
+ const byCursor = new Map(payloads.map((payload) => [String(payload.cursorId), payload]));
19950
+ for (const row of missing) {
19951
+ const payload = byCursor.get(String(row.cursorId));
19952
+ for (const [preview, column] of PREVIEW_PAYLOADS$2) if (needsPreview(row[preview])) row[preview] = readPreview(row[preview], payload?.[column]);
19953
+ }
19954
+ }
19790
19955
  function traceRowToResult(row) {
19791
19956
  return {
19792
19957
  traceId: String(row.traceId),
@@ -19796,7 +19961,9 @@ function traceRowToResult(row) {
19796
19961
  parentSpanId: row.parentSpanId ?? null,
19797
19962
  createdAt: asIsoTimestamp$1(row.startedAt),
19798
19963
  metadata: row.metadata ?? null,
19799
- inputPreview: coreStorage.buildInputPreview(row.input) ?? null,
19964
+ inputPreview: readPreview(row.inputPreview),
19965
+ outputPreview: readPreview(row.outputPreview),
19966
+ errorPreview: readPreview(row.errorPreview),
19800
19967
  threadId: row.threadId == null ? null : String(row.threadId),
19801
19968
  resourceId: row.resourceId == null ? null : String(row.resourceId),
19802
19969
  startedAt: asIsoTimestamp$1(row.startedAt),
@@ -19824,6 +19991,7 @@ async function queryTraces(client, schema, plan, timeoutMs) {
19824
19991
  rows = await transaction.any(query.text, query.values);
19825
19992
  }
19826
19993
  const visible = rows.slice(0, plan.limit);
19994
+ await fillMissingTracePreviews(transaction, schema, visible, deadline);
19827
19995
  const last = visible.at(-1);
19828
19996
  return coreStorage.traceQueryResponseSchema.parse({
19829
19997
  traces: visible.map(traceRowToResult),
@@ -19853,6 +20021,7 @@ async function queryTraces(client, schema, plan, timeoutMs) {
19853
20021
  if (remainingTimeoutMs <= 0) throw new coreStorage.TraceQueryExecutionError();
19854
20022
  await transaction.query(`SELECT set_config('statement_timeout', $1, true)`, [`${remainingTimeoutMs}ms`]);
19855
20023
  const rows = await transaction.any(dataQuery.text, dataQuery.values);
20024
+ await fillMissingTracePreviews(transaction, schema, rows, deadline);
19856
20025
  return {
19857
20026
  total: Number(countRows[0]?.count ?? 0),
19858
20027
  rows,
@@ -19872,7 +20041,12 @@ async function queryTraces(client, schema, plan, timeoutMs) {
19872
20041
  });
19873
20042
  }
19874
20043
  const query = compilePostgresTraceQuery(schema, plan);
19875
- const rows = await runWithPostgresTraceQueryTimeout(client, timeoutMs, (transaction) => transaction.any(query.text, query.values));
20044
+ const deadline = performance.now() + coreStorage.resolveTraceQueryTimeoutMs(timeoutMs);
20045
+ const rows = await runWithPostgresTraceQueryTimeout(client, timeoutMs, async (transaction) => {
20046
+ const rows = await transaction.any(query.text, query.values);
20047
+ if (plan.result === "traces") await fillMissingTracePreviews(transaction, schema, rows.slice(0, plan.limit), deadline);
20048
+ return rows;
20049
+ });
19876
20050
  const visibleRows = rows.slice(0, plan.limit);
19877
20051
  if (plan.result === "groups") {
19878
20052
  const groups = visibleRows.map((row) => ({ threadId: String(row.threadId) }));
@@ -20029,6 +20203,29 @@ function compilePostgresSpanQuery(schema, plan) {
20029
20203
  values
20030
20204
  };
20031
20205
  }
20206
+ const PREVIEW_PAYLOADS$1 = [
20207
+ ["inputPreview", "input"],
20208
+ ["outputPreview", "output"],
20209
+ ["errorPreview", "error"]
20210
+ ];
20211
+ /**
20212
+ * Stored previews of a hydrated row, built from the payload where the stored preview is NULL.
20213
+ * jsonb comes back decoded, so the fallback uses `buildPreview(value)`. jsonb normalizes object key
20214
+ * order, so a compact-JSON fallback preview can differ from what the writer would have stored from
20215
+ * the original value; that is acceptable for rows written before the preview columns existed.
20216
+ */
20217
+ function toSpanQueryPayload(row) {
20218
+ const [inputPreview, outputPreview, errorPreview] = PREVIEW_PAYLOADS$1.map(([preview, payload]) => readPreview(row[preview], row[payload]));
20219
+ return {
20220
+ organizationId: row.organizationId ?? null,
20221
+ resourceId: row.resourceId ?? null,
20222
+ traceId: String(row.traceId),
20223
+ spanId: String(row.spanId),
20224
+ inputPreview,
20225
+ outputPreview,
20226
+ errorPreview
20227
+ };
20228
+ }
20032
20229
  async function querySpans(client, schema, plan, timeoutMs) {
20033
20230
  const deadline = performance.now() + coreStorage.resolveTraceQueryTimeoutMs(timeoutMs);
20034
20231
  return runWithPostgresTraceQueryTimeout(client, timeoutMs, async (tx) => {
@@ -20053,9 +20250,9 @@ async function querySpans(client, schema, plan, timeoutMs) {
20053
20250
  payloadValues.push(row.version);
20054
20251
  return `(${key} AND "cursorId" = $${payloadValues.length}::bigint)`;
20055
20252
  }).join(" OR ");
20056
- const payloads = await tx.any(`SELECT ${identities},
20057
- left(input::text, ${coreStorage.SPAN_QUERY_MAX_PREVIEW_CHARACTERS + 1}) AS "inputPreview", left(output::text, ${coreStorage.SPAN_QUERY_MAX_PREVIEW_CHARACTERS + 1}) AS "outputPreview"
20058
- FROM ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} WHERE ${payloadWhere}`, payloadValues);
20253
+ const payloads = (await tx.any(`SELECT ${identities},
20254
+ ${PREVIEW_PAYLOADS$1.map(([preview, payload]) => `"${preview}", CASE WHEN ${missingPreviewSql(`"${preview}"`)} THEN "${payload}" END AS "${payload}"`).join(",\n ")}
20255
+ FROM ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} WHERE ${payloadWhere}`, payloadValues)).map(toSpanQueryPayload);
20059
20256
  await remaining();
20060
20257
  const metricValues = [];
20061
20258
  const metricWhere = selectedKeys(page, metricValues);
@@ -20066,7 +20263,7 @@ async function querySpans(client, schema, plan, timeoutMs) {
20066
20263
  ORDER BY ${identities}, "metricId", "cursorId" DESC) m
20067
20264
  LIMIT ${coreStorage.SPAN_QUERY_MAX_COST_METRICS + 1}`, metricValues);
20068
20265
  if (metrics.length > coreStorage.SPAN_QUERY_MAX_COST_METRICS) throw new coreStorage.TraceQueryResourceLimitError();
20069
- return coreStorage.buildSpanQueryResponse(plan, selected, payloads, metrics);
20266
+ return buildSpanQueryResponse(plan, selected, payloads, metrics);
20070
20267
  }, { repeatableRead: true });
20071
20268
  }
20072
20269
  //#endregion
@@ -20531,16 +20728,73 @@ function buildListTracesFilters(filters, spanTable, nextParamIdx) {
20531
20728
  * array-of-strings representation here.
20532
20729
  */
20533
20730
  const SPAN_SELECT_COLUMNS_ALIASED = SPAN_SELECT_COLUMNS.replace(/\n/g, " ").split(",").map((c) => `r.${c.trim()}`).join(", ");
20731
+ const FULL_PROJECTION = {
20732
+ columns: SPAN_SELECT_COLUMNS_ALIASED,
20733
+ mapRows: (_client, _span, rows) => toTraceSpans(rows.map(rowToSpanRecord))
20734
+ };
20735
+ /**
20736
+ * Columns a light trace list renders: the timeline fields, memory context, metadata and the stored
20737
+ * previews. The `input` / `output` payloads stay off the read path; rows written before the
20738
+ * previews were stored get theirs from the payloads, read for just those rows.
20739
+ */
20740
+ const TRACE_LIGHT_SELECT_COLUMNS = [
20741
+ ...SPAN_LIGHT_SELECT_COLUMN_NAMES,
20742
+ "threadId",
20743
+ "resourceId",
20744
+ "metadataRaw",
20745
+ "inputPreview",
20746
+ "outputPreview",
20747
+ "errorPreview"
20748
+ ].map((column) => `r."${column}"`).join(", ");
20749
+ const PREVIEW_PAYLOADS = [
20750
+ ["inputPreview", "input"],
20751
+ ["outputPreview", "output"],
20752
+ ["errorPreview", "error"]
20753
+ ];
20754
+ /**
20755
+ * Builds the previews of rows written before previews were stored (NULL preview) from those rows'
20756
+ * payloads. Only the flagged rows' payloads are read, so this fades out as old rows age out.
20757
+ *
20758
+ * `jsonb` comes back decoded with its keys reordered, so a compact-JSON preview built here can
20759
+ * differ in key order from the one the writer would have stored. Acceptable for old rows.
20760
+ */
20761
+ async function fillMissingPreviews(client, span, rows) {
20762
+ const missing = rows.filter((row) => PREVIEW_PAYLOADS.some(([preview]) => needsPreview(row[preview])));
20763
+ if (!missing.length) return;
20764
+ const payloads = await client.manyOrNone(`SELECT e."cursorId"::text AS "cursorId",
20765
+ ${PREVIEW_PAYLOADS.map(([preview, payload]) => `${missing.some((row) => needsPreview(row[preview])) ? `e."${payload}"` : "NULL"} AS "${payload}"`).join(",\n ")}
20766
+ FROM ${span} e
20767
+ WHERE (e."traceId", e."cursorId") IN (SELECT * FROM unnest($1::text[], $2::bigint[]))`, [missing.map((row) => String(row.traceId)), missing.map((row) => String(row.cursorId))]);
20768
+ const byCursor = new Map(payloads.map((payload) => [String(payload.cursorId), payload]));
20769
+ for (const row of missing) {
20770
+ const payload = byCursor.get(String(row.cursorId));
20771
+ for (const [preview, column] of PREVIEW_PAYLOADS) if (needsPreview(row[preview])) row[preview] = readPreview(row[preview], payload?.[column]);
20772
+ }
20773
+ }
20774
+ const LIGHT_PROJECTION = {
20775
+ columns: TRACE_LIGHT_SELECT_COLUMNS,
20776
+ mapRows: async (client, span, rows) => {
20777
+ await fillMissingPreviews(client, span, rows);
20778
+ return rows.map(rowToLightTraceRecord);
20779
+ }
20780
+ };
20534
20781
  async function listTraces(client, schema, args) {
20782
+ return listTraceRows(client, schema, args, FULL_PROJECTION);
20783
+ }
20784
+ /** Trace list projecting only the columns a list renders, with stored previews instead of payloads. */
20785
+ async function listTracesLight(client, schema, args) {
20786
+ return listTraceRows(client, schema, args, LIGHT_PROJECTION);
20787
+ }
20788
+ async function listTraceRows(client, schema, args, projection) {
20535
20789
  const { mode, filters, pagination, orderBy, after, limit } = listTracesArgsSchema.parse(args);
20536
20790
  const span = qualifiedTable(schema, TABLE_SPAN_EVENTS);
20537
20791
  if (mode === "delta") {
20538
20792
  assertDeltaPollingEnabled();
20539
- return listTracesDelta(client, span, filters, after, limit);
20793
+ return listTracesDelta(client, span, projection, filters, after, limit);
20540
20794
  }
20541
- return listTracesPage(client, span, filters, pagination.page, pagination.perPage, orderBy.field, orderBy.direction);
20795
+ return listTracesPage(client, span, projection, filters, pagination.page, pagination.perPage, orderBy.field, orderBy.direction);
20542
20796
  }
20543
- async function listTracesPage(client, span, filters, page, perPage, orderField, orderDir) {
20797
+ async function listTracesPage(client, span, projection, filters, page, perPage, orderField, orderDir) {
20544
20798
  const { conditions, params, nextParamIdx } = buildListTracesFilters(filters, span, 1);
20545
20799
  let i = nextParamIdx;
20546
20800
  const whereClause = `WHERE ${conditions.join(" AND ")}`;
@@ -20548,15 +20802,18 @@ async function listTracesPage(client, span, filters, page, perPage, orderField,
20548
20802
  const countRow = await client.oneOrNone(`SELECT COUNT(*)::text AS count FROM ${span} r ${whereClause}`, params);
20549
20803
  const count = Number(countRow?.count ?? 0);
20550
20804
  let spans = [];
20551
- if (count > 0) spans = (await client.manyOrNone(`SELECT ${SPAN_SELECT_COLUMNS_ALIASED}
20805
+ if (count > 0) {
20806
+ const rows = await client.manyOrNone(`SELECT ${projection.columns}
20552
20807
  FROM ${span} r
20553
20808
  ${whereClause}
20554
20809
  ${orderClause}
20555
20810
  LIMIT $${i++} OFFSET $${i++}`, [
20556
- ...params,
20557
- perPage,
20558
- page * perPage
20559
- ])).map(rowToSpanRecord);
20811
+ ...params,
20812
+ perPage,
20813
+ page * perPage
20814
+ ]);
20815
+ spans = await projection.mapRows(client, span, rows);
20816
+ }
20560
20817
  const deltaCursor = deltaPollingFeatureEnabled() ? await readTracesStreamHeadCursor(client, span, filters) : void 0;
20561
20818
  return {
20562
20819
  pagination: {
@@ -20565,11 +20822,11 @@ async function listTracesPage(client, span, filters, page, perPage, orderField,
20565
20822
  perPage,
20566
20823
  hasMore: (page + 1) * perPage < count
20567
20824
  },
20568
- spans: toTraceSpans(spans),
20825
+ spans,
20569
20826
  ...deltaCursor !== void 0 ? { deltaCursor } : {}
20570
20827
  };
20571
20828
  }
20572
- async function listTracesDelta(client, span, filters, after, limit) {
20829
+ async function listTracesDelta(client, span, projection, filters, after, limit) {
20573
20830
  if (after === void 0) {
20574
20831
  const deltaCursor = await readTracesStreamHeadCursor(client, span, filters);
20575
20832
  return {
@@ -20589,7 +20846,7 @@ async function listTracesDelta(client, span, filters, after, limit) {
20589
20846
  params.push(afterCursor.xactId, afterCursor.cursorId);
20590
20847
  conditions.push(`r."xactId" < $${i++}::xid8`);
20591
20848
  params.push(safeHorizon);
20592
- const rows = await client.manyOrNone(`SELECT ${SPAN_SELECT_COLUMNS_ALIASED}
20849
+ const rows = await client.manyOrNone(`SELECT ${projection.columns}
20593
20850
  FROM ${span} r
20594
20851
  WHERE ${conditions.join(" AND ")}
20595
20852
  ORDER BY r."xactId" ASC, r."cursorId" ASC
@@ -20598,7 +20855,7 @@ async function listTracesDelta(client, span, filters, after, limit) {
20598
20855
  const visible = rows.slice(0, limit);
20599
20856
  const deltaCursor = visible.length > 0 ? encodeDeltaCursor(visible[visible.length - 1].xactId, visible[visible.length - 1].cursorId) : encodeDeltaCursor(safeHorizon, 0);
20600
20857
  return {
20601
- spans: toTraceSpans(visible.map(rowToSpanRecord)),
20858
+ spans: await projection.mapRows(client, span, visible),
20602
20859
  delta: {
20603
20860
  limit,
20604
20861
  hasMore
@@ -21309,6 +21566,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21309
21566
  async listTraces(args) {
21310
21567
  return this.#run("LIST_TRACES", () => listTraces(this.#readClient, this.#schema, args));
21311
21568
  }
21569
+ async listTracesLight(args) {
21570
+ return this.#run("LIST_TRACES_LIGHT", () => listTracesLight(this.#readClient, this.#schema, args));
21571
+ }
21312
21572
  async querySpans(plan) {
21313
21573
  return this.#run("QUERY_SPANS", () => querySpans(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
21314
21574
  }