@mastra/duckdb 1.3.0-alpha.0 → 1.3.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.
@@ -2434,11 +2434,91 @@ function rowToSpanRecord(row) {
2434
2434
  updatedAt: null
2435
2435
  };
2436
2436
  }
2437
- function buildHasChildErrorClause(hasChildError) {
2437
+ function buildHasChildErrorClause(hasChildError, rootAlias) {
2438
2438
  if (hasChildError === void 0) return "";
2439
- const base = `SELECT 1 FROM reconstructed_spans c WHERE c.traceId = root_spans.traceId AND c.spanId != root_spans.spanId AND c.error IS NOT NULL`;
2439
+ const base = `SELECT 1 FROM span_events c WHERE c.traceId = ${rootAlias}.traceId AND c.spanId != ${rootAlias}.spanId AND c.error IS NOT NULL`;
2440
2440
  return hasChildError ? `EXISTS (${base})` : `NOT EXISTS (${base})`;
2441
2441
  }
2442
+ var PREFILTER_KEYS = /* @__PURE__ */ new Set([
2443
+ "traceId",
2444
+ "spanId",
2445
+ "parentSpanId",
2446
+ "name",
2447
+ "spanType",
2448
+ "source",
2449
+ "entityType",
2450
+ "entityId",
2451
+ "entityName",
2452
+ "entityVersionId",
2453
+ "experimentId",
2454
+ "userId",
2455
+ "organizationId",
2456
+ "resourceId",
2457
+ "runId",
2458
+ "sessionId",
2459
+ "threadId",
2460
+ "requestId",
2461
+ "environment",
2462
+ "serviceName"
2463
+ ]);
2464
+ var SAFE_PREFILTER_ORDER_FIELDS = /* @__PURE__ */ new Set(["startedAt"]);
2465
+ function intersectTimestampRange(existing, incoming) {
2466
+ if (!existing) return { ...incoming };
2467
+ const merged = { ...existing };
2468
+ if (incoming.start !== void 0) {
2469
+ if (merged.start === void 0 || incoming.start.getTime() > merged.start.getTime()) {
2470
+ merged.start = incoming.start;
2471
+ merged.startExclusive = incoming.startExclusive;
2472
+ } else if (incoming.start.getTime() === merged.start.getTime()) {
2473
+ merged.startExclusive = (merged.startExclusive ?? false) || (incoming.startExclusive ?? false);
2474
+ }
2475
+ }
2476
+ if (incoming.end !== void 0) {
2477
+ if (merged.end === void 0 || incoming.end.getTime() < merged.end.getTime()) {
2478
+ merged.end = incoming.end;
2479
+ merged.endExclusive = incoming.endExclusive;
2480
+ } else if (incoming.end.getTime() === merged.end.getTime()) {
2481
+ merged.endExclusive = (merged.endExclusive ?? false) || (incoming.endExclusive ?? false);
2482
+ }
2483
+ }
2484
+ return merged;
2485
+ }
2486
+ function partitionAnchorFilters(filters) {
2487
+ const prefilter = {};
2488
+ const postAgg = {};
2489
+ let hasChildError;
2490
+ for (const [key, value] of Object.entries(filters)) {
2491
+ if (value === void 0 || value === null) continue;
2492
+ if (key === "hasChildError") {
2493
+ if (typeof value === "boolean") hasChildError = value;
2494
+ continue;
2495
+ }
2496
+ if (key === "startedAt") {
2497
+ prefilter.timestamp = intersectTimestampRange(
2498
+ prefilter.timestamp,
2499
+ value
2500
+ );
2501
+ continue;
2502
+ }
2503
+ if (key === "endedAt") {
2504
+ postAgg.endedAt = value;
2505
+ const dateRange = value;
2506
+ if (dateRange?.end) {
2507
+ prefilter.timestamp = intersectTimestampRange(prefilter.timestamp, {
2508
+ end: dateRange.end,
2509
+ endExclusive: dateRange.endExclusive
2510
+ });
2511
+ }
2512
+ continue;
2513
+ }
2514
+ if (PREFILTER_KEYS.has(key)) {
2515
+ prefilter[key] = value;
2516
+ continue;
2517
+ }
2518
+ postAgg[key] = value;
2519
+ }
2520
+ return { prefilter, postAgg, hasChildError };
2521
+ }
2442
2522
  function toValuesTuple(row) {
2443
2523
  return [
2444
2524
  v(row.eventType),
@@ -2617,44 +2697,82 @@ async function listTraces(db, args) {
2617
2697
  const page = Number(args.pagination?.page ?? 0);
2618
2698
  const perPage = Number(args.pagination?.perPage ?? 10);
2619
2699
  const orderBy = { field: args.orderBy?.field ?? "startedAt", direction: args.orderBy?.direction ?? "DESC" };
2620
- const { clause: filterClause, params: filterParams } = buildWhereClause(filters);
2621
- const orderByClause = buildOrderByClause(orderBy);
2622
- const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
2623
- const filterParts = [];
2624
- if (filterClause) filterParts.push(filterClause.replace(/^WHERE\s+/i, ""));
2625
- const hasChildError = typeof filters.hasChildError === "boolean" ? filters.hasChildError : void 0;
2626
- const childErrorClause = buildHasChildErrorClause(hasChildError);
2627
- if (childErrorClause) filterParts.push(childErrorClause);
2628
- const combinedFilterClause = filterParts.length > 0 ? `WHERE ${filterParts.join(" AND ")}` : "";
2629
- const cteSql = `
2630
- WITH reconstructed_spans AS (
2700
+ const { prefilter, postAgg, hasChildError } = partitionAnchorFilters(filters);
2701
+ const { clause: prefilterClause, params: prefilterParams } = buildWhereClause(prefilter);
2702
+ const prefilterParts = [`eventType = 'start'`, `parentSpanId IS NULL`];
2703
+ if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
2704
+ const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
2705
+ const outerAlias = "outer_root";
2706
+ const orderDir = orderBy.direction.toUpperCase();
2707
+ if (orderDir !== "ASC" && orderDir !== "DESC") {
2708
+ throw new Error(`Invalid sort direction: ${orderBy.direction}`);
2709
+ }
2710
+ const canOrderInPrefilter = SAFE_PREFILTER_ORDER_FIELDS.has(orderBy.field);
2711
+ const hasPostAggFilters = Object.keys(postAgg).length > 0 || hasChildError !== void 0 || !canOrderInPrefilter;
2712
+ if (!hasPostAggFilters) {
2713
+ const prefilterOrderBy = `ORDER BY timestamp ${orderDir}`;
2714
+ const offset = page * perPage;
2715
+ const countSql2 = `
2716
+ SELECT COUNT(*) as total
2717
+ FROM span_events AS ${outerAlias}
2718
+ ${prefilterWhere}
2719
+ `;
2720
+ const countResult2 = await db.query(countSql2, prefilterParams);
2721
+ const total2 = Number(countResult2[0]?.total ?? 0);
2722
+ const pageSql = `
2723
+ WITH page_roots AS (
2724
+ SELECT traceId, spanId
2725
+ FROM span_events AS ${outerAlias}
2726
+ ${prefilterWhere}
2727
+ ${prefilterOrderBy}
2728
+ LIMIT ? OFFSET ?
2729
+ )
2631
2730
  ${SPAN_RECONSTRUCT_SELECT}
2731
+ WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM page_roots)
2632
2732
  GROUP BY traceId, spanId
2733
+ ${buildOrderByClause(orderBy)}
2734
+ `;
2735
+ const rows2 = await db.query(pageSql, [...prefilterParams, perPage, offset]);
2736
+ const spans2 = rows2.map((row) => rowToSpanRecord(row));
2737
+ return {
2738
+ pagination: { total: total2, page, perPage, hasMore: (page + 1) * perPage < total2 },
2739
+ spans: toTraceSpans(spans2)
2740
+ };
2741
+ }
2742
+ const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
2743
+ const postAggParts = [];
2744
+ if (postAggClause) postAggParts.push(postAggClause.replace(/^WHERE\s+/i, ""));
2745
+ const childErrorClause = buildHasChildErrorClause(hasChildError, "root_spans");
2746
+ if (childErrorClause) postAggParts.push(childErrorClause);
2747
+ const postAggWhere = postAggParts.length > 0 ? `WHERE ${postAggParts.join(" AND ")}` : "";
2748
+ const cteSql = `
2749
+ WITH candidate_roots AS (
2750
+ SELECT traceId, spanId
2751
+ FROM span_events AS ${outerAlias}
2752
+ ${prefilterWhere}
2633
2753
  ),
2634
2754
  root_spans AS (
2635
- SELECT * FROM reconstructed_spans
2636
- WHERE parentSpanId IS NULL
2755
+ ${SPAN_RECONSTRUCT_SELECT}
2756
+ WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_roots)
2757
+ GROUP BY traceId, spanId
2637
2758
  )
2638
2759
  `;
2760
+ const orderByClause = buildOrderByClause(orderBy);
2761
+ const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
2639
2762
  const countSql = `
2640
2763
  ${cteSql}
2641
- SELECT COUNT(*) as total FROM root_spans ${combinedFilterClause}
2764
+ SELECT COUNT(*) as total FROM root_spans ${postAggWhere}
2642
2765
  `;
2643
- const countResult = await db.query(countSql, filterParams);
2766
+ const countResult = await db.query(countSql, [...prefilterParams, ...postAggParams]);
2644
2767
  const total = Number(countResult[0]?.total ?? 0);
2645
2768
  const dataSql = `
2646
2769
  ${cteSql}
2647
- SELECT * FROM root_spans ${combinedFilterClause} ${orderByClause} ${paginationClause}
2770
+ SELECT * FROM root_spans ${postAggWhere} ${orderByClause} ${paginationClause}
2648
2771
  `;
2649
- const rows = await db.query(dataSql, [...filterParams, ...paginationParams]);
2772
+ const rows = await db.query(dataSql, [...prefilterParams, ...postAggParams, ...paginationParams]);
2650
2773
  const spans = rows.map((row) => rowToSpanRecord(row));
2651
2774
  return {
2652
- pagination: {
2653
- total,
2654
- page,
2655
- perPage,
2656
- hasMore: (page + 1) * perPage < total
2657
- },
2775
+ pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
2658
2776
  spans: toTraceSpans(spans)
2659
2777
  };
2660
2778
  }
@@ -2680,37 +2798,92 @@ async function listBranches(db, args) {
2680
2798
  const page = Number(args.pagination?.page ?? 0);
2681
2799
  const perPage = Number(args.pagination?.perPage ?? 10);
2682
2800
  const orderBy = { field: args.orderBy?.field ?? "startedAt", direction: args.orderBy?.direction ?? "DESC" };
2683
- const { spanType, ...passthroughFilters } = filters;
2684
- const { clause: filterClause, params: filterParams } = buildWhereClause(passthroughFilters);
2685
- const orderByClause = buildOrderByClause(orderBy);
2686
- const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
2687
- let prefilterClause;
2688
- let prefilterParams;
2689
- if (typeof spanType === "string") {
2690
- if (!BRANCH_SPAN_TYPES.includes(spanType)) {
2801
+ const userSpanType = filters.spanType;
2802
+ if (typeof userSpanType === "string" && !BRANCH_SPAN_TYPES.includes(userSpanType)) {
2803
+ return {
2804
+ pagination: { total: 0, page, perPage, hasMore: false },
2805
+ branches: []
2806
+ };
2807
+ }
2808
+ const { spanType: _spanType, ...rest } = filters;
2809
+ const { prefilter, postAgg} = partitionAnchorFilters(rest);
2810
+ const { clause: prefilterClause, params: prefilterFilterParams } = buildWhereClause(prefilter);
2811
+ const prefilterParts = [`eventType = 'start'`];
2812
+ let spanTypeParams;
2813
+ if (typeof userSpanType === "string") {
2814
+ prefilterParts.push(`spanType = ?`);
2815
+ spanTypeParams = [userSpanType];
2816
+ } else {
2817
+ prefilterParts.push(`spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS})`);
2818
+ spanTypeParams = [...BRANCH_SPAN_TYPES];
2819
+ }
2820
+ if (prefilterClause) prefilterParts.push(prefilterClause.replace(/^WHERE\s+/i, ""));
2821
+ const prefilterWhere = `WHERE ${prefilterParts.join(" AND ")}`;
2822
+ const prefilterParams = [...spanTypeParams, ...prefilterFilterParams];
2823
+ const outerAlias = "outer_anchor";
2824
+ const orderDir = orderBy.direction.toUpperCase();
2825
+ if (orderDir !== "ASC" && orderDir !== "DESC") {
2826
+ throw new Error(`Invalid sort direction: ${orderBy.direction}`);
2827
+ }
2828
+ const canOrderInPrefilter = SAFE_PREFILTER_ORDER_FIELDS.has(orderBy.field);
2829
+ const hasPostAggFilters = Object.keys(postAgg).length > 0 || !canOrderInPrefilter;
2830
+ if (!hasPostAggFilters) {
2831
+ const prefilterOrderBy = `ORDER BY timestamp ${orderDir}`;
2832
+ const offset = page * perPage;
2833
+ const countSql2 = `
2834
+ SELECT COUNT(*) as total
2835
+ FROM span_events AS ${outerAlias}
2836
+ ${prefilterWhere}
2837
+ `;
2838
+ const countResult2 = await db.query(countSql2, prefilterParams);
2839
+ const total2 = Number(countResult2[0]?.total ?? 0);
2840
+ if (total2 === 0) {
2691
2841
  return {
2692
2842
  pagination: { total: 0, page, perPage, hasMore: false },
2693
2843
  branches: []
2694
2844
  };
2695
2845
  }
2696
- prefilterClause = `WHERE spanType = ?`;
2697
- prefilterParams = [spanType];
2698
- } else {
2699
- prefilterClause = `WHERE spanType IN (${BRANCH_SPAN_TYPE_PLACEHOLDERS})`;
2700
- prefilterParams = [...BRANCH_SPAN_TYPES];
2846
+ const pageSql = `
2847
+ WITH page_anchors AS (
2848
+ SELECT traceId, spanId
2849
+ FROM span_events AS ${outerAlias}
2850
+ ${prefilterWhere}
2851
+ ${prefilterOrderBy}
2852
+ LIMIT ? OFFSET ?
2853
+ )
2854
+ ${SPAN_RECONSTRUCT_SELECT}
2855
+ WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM page_anchors)
2856
+ GROUP BY traceId, spanId
2857
+ ${buildOrderByClause(orderBy)}
2858
+ `;
2859
+ const rows2 = await db.query(pageSql, [...prefilterParams, perPage, offset]);
2860
+ const spans2 = rows2.map((row) => rowToSpanRecord(row));
2861
+ return {
2862
+ pagination: { total: total2, page, perPage, hasMore: (page + 1) * perPage < total2 },
2863
+ branches: toTraceSpans(spans2)
2864
+ };
2701
2865
  }
2866
+ const { clause: postAggClause, params: postAggParams } = buildWhereClause(postAgg);
2867
+ const postAggWhere = postAggClause ? postAggClause : "";
2868
+ const orderByClause = buildOrderByClause(orderBy);
2869
+ const { clause: paginationClause, params: paginationParams } = buildPaginationClause({ page, perPage });
2702
2870
  const cteSql = `
2703
- WITH branch_anchors AS (
2871
+ WITH candidate_anchors AS (
2872
+ SELECT traceId, spanId
2873
+ FROM span_events AS ${outerAlias}
2874
+ ${prefilterWhere}
2875
+ ),
2876
+ branch_anchors AS (
2704
2877
  ${SPAN_RECONSTRUCT_SELECT}
2705
- ${prefilterClause}
2878
+ WHERE (traceId, spanId) IN (SELECT traceId, spanId FROM candidate_anchors)
2706
2879
  GROUP BY traceId, spanId
2707
2880
  )
2708
2881
  `;
2709
2882
  const countSql = `
2710
2883
  ${cteSql}
2711
- SELECT COUNT(*) as total FROM branch_anchors ${filterClause}
2884
+ SELECT COUNT(*) as total FROM branch_anchors ${postAggWhere}
2712
2885
  `;
2713
- const countResult = await db.query(countSql, [...prefilterParams, ...filterParams]);
2886
+ const countResult = await db.query(countSql, [...prefilterParams, ...postAggParams]);
2714
2887
  const total = Number(countResult[0]?.total ?? 0);
2715
2888
  if (total === 0) {
2716
2889
  return {
@@ -2720,17 +2893,12 @@ async function listBranches(db, args) {
2720
2893
  }
2721
2894
  const dataSql = `
2722
2895
  ${cteSql}
2723
- SELECT * FROM branch_anchors ${filterClause} ${orderByClause} ${paginationClause}
2896
+ SELECT * FROM branch_anchors ${postAggWhere} ${orderByClause} ${paginationClause}
2724
2897
  `;
2725
- const rows = await db.query(dataSql, [...prefilterParams, ...filterParams, ...paginationParams]);
2898
+ const rows = await db.query(dataSql, [...prefilterParams, ...postAggParams, ...paginationParams]);
2726
2899
  const spans = rows.map((row) => rowToSpanRecord(row));
2727
2900
  return {
2728
- pagination: {
2729
- total,
2730
- page,
2731
- perPage,
2732
- hasMore: (page + 1) * perPage < total
2733
- },
2901
+ pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
2734
2902
  branches: toTraceSpans(spans)
2735
2903
  };
2736
2904
  }
@@ -2956,5 +3124,5 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
2956
3124
  };
2957
3125
 
2958
3126
  export { ObservabilityStorageDuckDB };
2959
- //# sourceMappingURL=observability-2R7ZOHEZ.js.map
2960
- //# sourceMappingURL=observability-2R7ZOHEZ.js.map
3127
+ //# sourceMappingURL=observability-7LL4LLXY.js.map
3128
+ //# sourceMappingURL=observability-7LL4LLXY.js.map