@assistant-ui/react-o11y 0.0.46 → 0.0.47

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.
@@ -1 +1 @@
1
- {"version":3,"file":"SpanResource.d.ts","sourceRoot":"","sources":["../../src/resources/SpanResource.tsx"],"names":[],"mappings":"AAAA,0BAAuB;AAIvB,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGzE,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAoQF,eAAO,MAAM,YAAY;WAlEhB,QAAQ,EAAE;GAkEkC,CAAC"}
1
+ {"version":3,"file":"SpanResource.d.ts","sourceRoot":"","sources":["../../src/resources/SpanResource.tsx"],"names":[],"mappings":"AAAA,0BAAuB;AAIvB,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGzE,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAoQF,eAAO,MAAM,YAAY;WAnEhB,QAAQ,EAAE;GAmEkC,CAAC"}
@@ -76,19 +76,9 @@ function enrichSpans(rawSpans) {
76
76
  if (min === Infinity) min = Date.now();
77
77
  if (max === -Infinity) max = Date.now();
78
78
  if (max === min) max = min + 100;
79
- return {
80
- allSpans,
81
- parents,
82
- timeRange: {
83
- min,
84
- max
85
- }
86
- };
87
- }
88
- function buildFlatList(allSpans, parents, collapsedIds) {
89
79
  const children = /* @__PURE__ */ new Map();
90
80
  for (const span of allSpans.values()) {
91
- const parent = parents.get(span.id) ?? null;
81
+ const parent = span.parentSpanId;
92
82
  let group = children.get(parent);
93
83
  if (!group) {
94
84
  group = [];
@@ -97,6 +87,16 @@ function buildFlatList(allSpans, parents, collapsedIds) {
97
87
  group.push(span);
98
88
  }
99
89
  for (const group of children.values()) group.sort((a, b) => a.startedAt - b.startedAt);
90
+ return {
91
+ allSpans,
92
+ children,
93
+ timeRange: {
94
+ min,
95
+ max
96
+ }
97
+ };
98
+ }
99
+ function buildFlatList(children, collapsedIds) {
100
100
  const result = [];
101
101
  const stack = [];
102
102
  const roots = children.get(null) ?? [];
@@ -104,10 +104,10 @@ function buildFlatList(allSpans, parents, collapsedIds) {
104
104
  while (stack.length > 0) {
105
105
  const span = stack.pop();
106
106
  const isCollapsed = collapsedIds.has(span.id);
107
- result.push({
107
+ result.push(isCollapsed ? {
108
108
  ...span,
109
109
  isCollapsed
110
- });
110
+ } : span);
111
111
  if (!isCollapsed) {
112
112
  const kids = children.get(span.id) ?? [];
113
113
  for (let index = kids.length - 1; index >= 0; index--) stack.push(kids[index]);
@@ -133,7 +133,7 @@ const useSpanChildResource = ({ span, timeRange, onToggleCollapse }) => {
133
133
  };
134
134
  const SpanChildResource = resource(useSpanChildResource);
135
135
  const useSpanResource = (t0) => {
136
- const $ = c(20);
136
+ const $ = c(21);
137
137
  const { spans } = t0;
138
138
  let t1;
139
139
  if ($[0] !== spans) {
@@ -141,19 +141,18 @@ const useSpanResource = (t0) => {
141
141
  $[0] = spans;
142
142
  $[1] = t1;
143
143
  } else t1 = $[1];
144
- const { allSpans, parents, timeRange } = t1;
144
+ const { allSpans, children, timeRange } = t1;
145
145
  const [collapsedIds, setCollapsedIds] = useState(_temp);
146
146
  let t2;
147
- if ($[2] !== allSpans || $[3] !== collapsedIds || $[4] !== parents) {
148
- t2 = buildFlatList(allSpans, parents, collapsedIds);
149
- $[2] = allSpans;
147
+ if ($[2] !== children || $[3] !== collapsedIds) {
148
+ t2 = buildFlatList(children, collapsedIds);
149
+ $[2] = children;
150
150
  $[3] = collapsedIds;
151
- $[4] = parents;
152
- $[5] = t2;
153
- } else t2 = $[5];
151
+ $[4] = t2;
152
+ } else t2 = $[4];
154
153
  const visibleSpans = t2;
155
154
  let t3;
156
- if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
155
+ if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
157
156
  t3 = (spanId) => {
158
157
  setCollapsedIds((prev) => {
159
158
  const next = new Set(prev);
@@ -162,22 +161,24 @@ const useSpanResource = (t0) => {
162
161
  return next;
163
162
  });
164
163
  };
165
- $[6] = t3;
166
- } else t3 = $[6];
164
+ $[5] = t3;
165
+ } else t3 = $[5];
167
166
  const toggleCollapse = t3;
168
167
  let t4;
169
- if ($[7] !== timeRange || $[8] !== visibleSpans) {
168
+ if ($[6] !== allSpans || $[7] !== timeRange || $[8] !== visibleSpans) {
170
169
  let t5;
171
- if ($[10] !== timeRange) {
170
+ if ($[10] !== allSpans || $[11] !== timeRange) {
172
171
  t5 = (span) => withKey(span.id, SpanChildResource({
173
172
  span,
174
173
  timeRange,
175
174
  onToggleCollapse: toggleCollapse
176
- }));
177
- $[10] = timeRange;
178
- $[11] = t5;
179
- } else t5 = $[11];
175
+ }), [allSpans, span.isCollapsed]);
176
+ $[10] = allSpans;
177
+ $[11] = timeRange;
178
+ $[12] = t5;
179
+ } else t5 = $[12];
180
180
  t4 = visibleSpans.map(t5);
181
+ $[6] = allSpans;
181
182
  $[7] = timeRange;
182
183
  $[8] = visibleSpans;
183
184
  $[9] = t4;
@@ -186,7 +187,7 @@ const useSpanResource = (t0) => {
186
187
  const t5 = timeRange.max - timeRange.min;
187
188
  const t6 = lookup.state.length > 0;
188
189
  let t7;
189
- if ($[12] !== lookup.state || $[13] !== t5 || $[14] !== t6 || $[15] !== timeRange) {
190
+ if ($[13] !== lookup.state || $[14] !== t5 || $[15] !== t6 || $[16] !== timeRange) {
190
191
  const rootState = {
191
192
  id: "__root__",
192
193
  parentSpanId: null,
@@ -203,23 +204,23 @@ const useSpanResource = (t0) => {
203
204
  timeRange
204
205
  };
205
206
  t7 = () => rootState;
206
- $[12] = lookup.state;
207
- $[13] = t5;
208
- $[14] = t6;
209
- $[15] = timeRange;
210
- $[16] = t7;
211
- } else t7 = $[16];
207
+ $[13] = lookup.state;
208
+ $[14] = t5;
209
+ $[15] = t6;
210
+ $[16] = timeRange;
211
+ $[17] = t7;
212
+ } else t7 = $[17];
212
213
  let t8;
213
- if ($[17] !== lookup.get || $[18] !== t7) {
214
+ if ($[18] !== lookup.get || $[19] !== t7) {
214
215
  t8 = {
215
216
  getState: t7,
216
217
  child: lookup.get,
217
218
  toggleCollapse: _temp2
218
219
  };
219
- $[17] = lookup.get;
220
- $[18] = t7;
221
- $[19] = t8;
222
- } else t8 = $[19];
220
+ $[18] = lookup.get;
221
+ $[19] = t7;
222
+ $[20] = t8;
223
+ } else t8 = $[20];
223
224
  return t8;
224
225
  };
225
226
  const SpanResource = resource(useSpanResource);
@@ -1 +1 @@
1
- {"version":3,"file":"SpanResource.js","names":["useMemo","useState","resource","withKey","useClientLookup","ClientOutput","SpanItemState","SpanState","SpanData","id","parentSpanId","name","type","status","startedAt","endedAt","latencyMs","calculateDepth","spanId","parents","Map","cache","cached","get","undefined","path","current","depth","currentDepth","push","index","length","set","normalizeSpanParents","spanMap","span","values","has","resolved","Set","keys","pathIds","add","enrichSpans","rawSpans","allSpans","timeRange","min","max","depthCache","childrenCount","Infinity","hasChildren","isCollapsed","end","Date","now","buildFlatList","collapsedIds","children","parent","group","sort","a","b","result","stack","roots","pop","kids","useSpanChildResource","onToggleCollapse","state","getState","child","Error","toggleCollapse","SpanChildResource","useSpanResource","t0","$","_c","spans","t1","setCollapsedIds","_temp","t2","visibleSpans","t3","Symbol","for","prev","next","delete","t4","t5","map","lookup","t6","t7","rootState","t8","_temp2","SpanResource","_c"],"sources":["../../src/resources/SpanResource.tsx"],"sourcesContent":["import \"../o11y-scope\";\n\nimport { useMemo, useState } from \"react\";\nimport { resource, withKey } from \"@assistant-ui/tap\";\nimport { useClientLookup, type ClientOutput } from \"@assistant-ui/store\";\nimport type { SpanItemState, SpanState } from \"../o11y-scope\";\n\nexport type SpanData = {\n id: string;\n parentSpanId: string | null;\n name: string;\n type: string;\n status: \"running\" | \"completed\" | \"failed\" | \"skipped\";\n startedAt: number;\n endedAt: number | null;\n latencyMs: number | null;\n};\n\nfunction calculateDepth(\n spanId: string,\n parents: Map<string, string | null>,\n cache: Map<string, number>,\n): number {\n const cached = cache.get(spanId);\n if (cached !== undefined) return cached;\n\n const path: string[] = [];\n let current: string | null = spanId;\n let depth = -1;\n\n while (current !== null) {\n const currentDepth = cache.get(current);\n if (currentDepth !== undefined) {\n depth = currentDepth;\n break;\n }\n\n path.push(current);\n current = parents.get(current) ?? null;\n }\n\n for (let index = path.length - 1; index >= 0; index--) {\n depth += 1;\n cache.set(path[index]!, depth);\n }\n\n return depth;\n}\n\nfunction normalizeSpanParents(\n spanMap: Map<string, SpanData>,\n): Map<string, string | null> {\n const parents = new Map<string, string | null>();\n for (const span of spanMap.values()) {\n parents.set(\n span.id,\n span.parentSpanId && spanMap.has(span.parentSpanId)\n ? span.parentSpanId\n : null,\n );\n }\n\n const resolved = new Set<string>();\n for (const spanId of spanMap.keys()) {\n if (resolved.has(spanId)) continue;\n\n const path: string[] = [];\n const pathIds = new Set<string>();\n let current: string | null = spanId;\n\n while (current !== null && !resolved.has(current)) {\n if (pathIds.has(current)) {\n parents.set(current, null);\n break;\n }\n\n path.push(current);\n pathIds.add(current);\n current = parents.get(current) ?? null;\n }\n\n for (const id of path) resolved.add(id);\n }\n\n return parents;\n}\n\nfunction enrichSpans(rawSpans: SpanData[]): {\n allSpans: Map<string, SpanItemState>;\n parents: Map<string, string | null>;\n timeRange: { min: number; max: number };\n} {\n const spanMap = new Map<string, SpanData>();\n for (const span of rawSpans) {\n spanMap.set(span.id, span);\n }\n\n const parents = normalizeSpanParents(spanMap);\n const depthCache = new Map<string, number>();\n const childrenCount = new Map<string, number>();\n for (const span of rawSpans) {\n const parentSpanId = parents.get(span.id);\n if (parentSpanId) {\n childrenCount.set(\n parentSpanId,\n (childrenCount.get(parentSpanId) ?? 0) + 1,\n );\n }\n }\n\n const allSpans = new Map<string, SpanItemState>();\n let min = Infinity;\n let max = -Infinity;\n\n for (const span of rawSpans) {\n const depth = calculateDepth(span.id, parents, depthCache);\n const hasChildren = (childrenCount.get(span.id) ?? 0) > 0;\n allSpans.set(span.id, {\n ...span,\n parentSpanId: parents.get(span.id) ?? null,\n depth,\n hasChildren,\n isCollapsed: false,\n });\n\n if (span.startedAt < min) min = span.startedAt;\n const end = span.endedAt ?? Date.now();\n if (end > max) max = end;\n }\n\n if (min === Infinity) min = Date.now();\n if (max === -Infinity) max = Date.now();\n if (max === min) max = min + 100;\n\n return { allSpans, parents, timeRange: { min, max } };\n}\n\nfunction buildFlatList(\n allSpans: Map<string, SpanItemState>,\n parents: Map<string, string | null>,\n collapsedIds: Set<string>,\n): SpanItemState[] {\n const children = new Map<string | null, SpanItemState[]>();\n for (const span of allSpans.values()) {\n const parent = parents.get(span.id) ?? null;\n let group = children.get(parent);\n if (!group) {\n group = [];\n children.set(parent, group);\n }\n group.push(span);\n }\n\n for (const group of children.values()) {\n group.sort((a, b) => a.startedAt - b.startedAt);\n }\n\n const result: SpanItemState[] = [];\n const stack: SpanItemState[] = [];\n const roots = children.get(null) ?? [];\n for (let index = roots.length - 1; index >= 0; index--) {\n stack.push(roots[index]!);\n }\n\n while (stack.length > 0) {\n const span = stack.pop()!;\n const isCollapsed = collapsedIds.has(span.id);\n result.push({ ...span, isCollapsed });\n\n if (!isCollapsed) {\n const kids = children.get(span.id) ?? [];\n for (let index = kids.length - 1; index >= 0; index--) {\n stack.push(kids[index]!);\n }\n }\n }\n\n return result;\n}\n\nconst useSpanChildResource = ({\n span,\n timeRange,\n onToggleCollapse,\n}: {\n span: SpanItemState;\n timeRange: { min: number; max: number };\n onToggleCollapse: (spanId: string) => void;\n}): ClientOutput<\"span\"> => {\n const state: SpanState = {\n ...span,\n children: [],\n timeRange,\n };\n return {\n getState: () => state,\n child: () => {\n throw new Error(\"child spans do not have children in flat mode\");\n },\n toggleCollapse: () => {\n onToggleCollapse(span.id);\n },\n };\n};\n\nconst SpanChildResource = resource(useSpanChildResource);\n\nconst useSpanResource = ({\n spans,\n}: {\n spans: SpanData[];\n}): ClientOutput<\"span\"> => {\n const { allSpans, parents, timeRange } = useMemo(\n () => enrichSpans(spans),\n [spans],\n );\n\n const [collapsedIds, setCollapsedIds] = useState<Set<string>>(\n () => new Set(),\n );\n\n const visibleSpans = useMemo(\n () => buildFlatList(allSpans, parents, collapsedIds),\n [allSpans, parents, collapsedIds],\n );\n\n const toggleCollapse = (spanId: string) => {\n setCollapsedIds((prev) => {\n const next = new Set(prev);\n if (next.has(spanId)) {\n next.delete(spanId);\n } else {\n next.add(spanId);\n }\n return next;\n });\n };\n\n const lookup = useClientLookup(\n visibleSpans.map((span) =>\n withKey(\n span.id,\n SpanChildResource({\n span,\n timeRange,\n onToggleCollapse: toggleCollapse,\n }),\n ),\n ),\n );\n\n const rootState: SpanState = {\n id: \"__root__\",\n parentSpanId: null,\n name: \"\",\n type: \"root\",\n status: \"completed\",\n startedAt: timeRange.min,\n endedAt: timeRange.max,\n latencyMs: timeRange.max - timeRange.min,\n depth: -1,\n hasChildren: lookup.state.length > 0,\n isCollapsed: false,\n children: lookup.state,\n timeRange,\n };\n\n return {\n getState: () => rootState,\n child: lookup.get,\n toggleCollapse: () => {\n // Root span collapse is a no-op\n },\n };\n};\n\nexport const SpanResource = resource(useSpanResource);\n"],"mappings":"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"}
1
+ {"version":3,"file":"SpanResource.js","names":["useMemo","useState","resource","withKey","useClientLookup","ClientOutput","SpanItemState","SpanState","SpanData","id","parentSpanId","name","type","status","startedAt","endedAt","latencyMs","calculateDepth","spanId","parents","Map","cache","cached","get","undefined","path","current","depth","currentDepth","push","index","length","set","normalizeSpanParents","spanMap","span","values","has","resolved","Set","keys","pathIds","add","enrichSpans","rawSpans","allSpans","children","timeRange","min","max","depthCache","childrenCount","Infinity","hasChildren","isCollapsed","end","Date","now","parent","group","sort","a","b","buildFlatList","collapsedIds","result","stack","roots","pop","kids","useSpanChildResource","onToggleCollapse","state","getState","child","Error","toggleCollapse","SpanChildResource","useSpanResource","t0","$","_c","spans","t1","setCollapsedIds","_temp","t2","visibleSpans","t3","Symbol","for","prev","next","delete","t4","t5","map","lookup","t6","t7","rootState","t8","_temp2","SpanResource","_c"],"sources":["../../src/resources/SpanResource.tsx"],"sourcesContent":["import \"../o11y-scope\";\n\nimport { useMemo, useState } from \"react\";\nimport { resource, withKey } from \"@assistant-ui/tap\";\nimport { useClientLookup, type ClientOutput } from \"@assistant-ui/store\";\nimport type { SpanItemState, SpanState } from \"../o11y-scope\";\n\nexport type SpanData = {\n id: string;\n parentSpanId: string | null;\n name: string;\n type: string;\n status: \"running\" | \"completed\" | \"failed\" | \"skipped\";\n startedAt: number;\n endedAt: number | null;\n latencyMs: number | null;\n};\n\nfunction calculateDepth(\n spanId: string,\n parents: Map<string, string | null>,\n cache: Map<string, number>,\n): number {\n const cached = cache.get(spanId);\n if (cached !== undefined) return cached;\n\n const path: string[] = [];\n let current: string | null = spanId;\n let depth = -1;\n\n while (current !== null) {\n const currentDepth = cache.get(current);\n if (currentDepth !== undefined) {\n depth = currentDepth;\n break;\n }\n\n path.push(current);\n current = parents.get(current) ?? null;\n }\n\n for (let index = path.length - 1; index >= 0; index--) {\n depth += 1;\n cache.set(path[index]!, depth);\n }\n\n return depth;\n}\n\nfunction normalizeSpanParents(\n spanMap: Map<string, SpanData>,\n): Map<string, string | null> {\n const parents = new Map<string, string | null>();\n for (const span of spanMap.values()) {\n parents.set(\n span.id,\n span.parentSpanId && spanMap.has(span.parentSpanId)\n ? span.parentSpanId\n : null,\n );\n }\n\n const resolved = new Set<string>();\n for (const spanId of spanMap.keys()) {\n if (resolved.has(spanId)) continue;\n\n const path: string[] = [];\n const pathIds = new Set<string>();\n let current: string | null = spanId;\n\n while (current !== null && !resolved.has(current)) {\n if (pathIds.has(current)) {\n parents.set(current, null);\n break;\n }\n\n path.push(current);\n pathIds.add(current);\n current = parents.get(current) ?? null;\n }\n\n for (const id of path) resolved.add(id);\n }\n\n return parents;\n}\n\nfunction enrichSpans(rawSpans: SpanData[]): {\n allSpans: Map<string, SpanItemState>;\n children: Map<string | null, SpanItemState[]>;\n timeRange: { min: number; max: number };\n} {\n const spanMap = new Map<string, SpanData>();\n for (const span of rawSpans) {\n spanMap.set(span.id, span);\n }\n\n const parents = normalizeSpanParents(spanMap);\n const depthCache = new Map<string, number>();\n const childrenCount = new Map<string, number>();\n for (const span of rawSpans) {\n const parentSpanId = parents.get(span.id);\n if (parentSpanId) {\n childrenCount.set(\n parentSpanId,\n (childrenCount.get(parentSpanId) ?? 0) + 1,\n );\n }\n }\n\n const allSpans = new Map<string, SpanItemState>();\n let min = Infinity;\n let max = -Infinity;\n\n for (const span of rawSpans) {\n const depth = calculateDepth(span.id, parents, depthCache);\n const hasChildren = (childrenCount.get(span.id) ?? 0) > 0;\n allSpans.set(span.id, {\n ...span,\n parentSpanId: parents.get(span.id) ?? null,\n depth,\n hasChildren,\n isCollapsed: false,\n });\n\n if (span.startedAt < min) min = span.startedAt;\n const end = span.endedAt ?? Date.now();\n if (end > max) max = end;\n }\n\n if (min === Infinity) min = Date.now();\n if (max === -Infinity) max = Date.now();\n if (max === min) max = min + 100;\n\n const children = new Map<string | null, SpanItemState[]>();\n for (const span of allSpans.values()) {\n const parent = span.parentSpanId;\n let group = children.get(parent);\n if (!group) {\n group = [];\n children.set(parent, group);\n }\n group.push(span);\n }\n\n for (const group of children.values()) {\n group.sort((a, b) => a.startedAt - b.startedAt);\n }\n\n return { allSpans, children, timeRange: { min, max } };\n}\n\nfunction buildFlatList(\n children: Map<string | null, SpanItemState[]>,\n collapsedIds: Set<string>,\n): SpanItemState[] {\n const result: SpanItemState[] = [];\n const stack: SpanItemState[] = [];\n const roots = children.get(null) ?? [];\n for (let index = roots.length - 1; index >= 0; index--) {\n stack.push(roots[index]!);\n }\n\n while (stack.length > 0) {\n const span = stack.pop()!;\n const isCollapsed = collapsedIds.has(span.id);\n result.push(isCollapsed ? { ...span, isCollapsed } : span);\n\n if (!isCollapsed) {\n const kids = children.get(span.id) ?? [];\n for (let index = kids.length - 1; index >= 0; index--) {\n stack.push(kids[index]!);\n }\n }\n }\n\n return result;\n}\n\nconst useSpanChildResource = ({\n span,\n timeRange,\n onToggleCollapse,\n}: {\n span: SpanItemState;\n timeRange: { min: number; max: number };\n onToggleCollapse: (spanId: string) => void;\n}): ClientOutput<\"span\"> => {\n const state: SpanState = {\n ...span,\n children: [],\n timeRange,\n };\n return {\n getState: () => state,\n child: () => {\n throw new Error(\"child spans do not have children in flat mode\");\n },\n toggleCollapse: () => {\n onToggleCollapse(span.id);\n },\n };\n};\n\nconst SpanChildResource = resource(useSpanChildResource);\n\nconst useSpanResource = ({\n spans,\n}: {\n spans: SpanData[];\n}): ClientOutput<\"span\"> => {\n const { allSpans, children, timeRange } = useMemo(\n () => enrichSpans(spans),\n [spans],\n );\n\n const [collapsedIds, setCollapsedIds] = useState<Set<string>>(\n () => new Set(),\n );\n\n const visibleSpans = useMemo(\n () => buildFlatList(children, collapsedIds),\n [children, collapsedIds],\n );\n\n const toggleCollapse = (spanId: string) => {\n setCollapsedIds((prev) => {\n const next = new Set(prev);\n if (next.has(spanId)) {\n next.delete(spanId);\n } else {\n next.add(spanId);\n }\n return next;\n });\n };\n\n const lookup = useClientLookup(\n visibleSpans.map((span) =>\n withKey(\n span.id,\n SpanChildResource({\n span,\n timeRange,\n onToggleCollapse: toggleCollapse,\n }),\n [allSpans, span.isCollapsed],\n ),\n ),\n );\n\n const rootState: SpanState = {\n id: \"__root__\",\n parentSpanId: null,\n name: \"\",\n type: \"root\",\n status: \"completed\",\n startedAt: timeRange.min,\n endedAt: timeRange.max,\n latencyMs: timeRange.max - timeRange.min,\n depth: -1,\n hasChildren: lookup.state.length > 0,\n isCollapsed: false,\n children: lookup.state,\n timeRange,\n };\n\n return {\n getState: () => rootState,\n child: lookup.get,\n toggleCollapse: () => {\n // Root span collapse is a no-op\n },\n };\n};\n\nexport const SpanResource = resource(useSpanResource);\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react-o11y",
3
- "version": "0.0.46",
3
+ "version": "0.0.47",
4
4
  "description": "Observability primitives for @assistant-ui",
5
5
  "keywords": [
6
6
  "observability",
@@ -28,9 +28,9 @@
28
28
  ],
29
29
  "sideEffects": false,
30
30
  "dependencies": {
31
- "@assistant-ui/store": "^0.3.17",
32
- "@assistant-ui/tap": "^0.9.21",
33
- "@radix-ui/react-primitive": "^2.1.10"
31
+ "@assistant-ui/store": "^0.3.18",
32
+ "@assistant-ui/tap": "^0.9.22",
33
+ "@radix-ui/react-primitive": "^2.1.11"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "@types/react": "*",
@@ -46,6 +46,7 @@
46
46
  "@assistant-ui/x-react18": "0.0.0",
47
47
  "@types/react": "^19.3.0",
48
48
  "@types/react-dom": "^19.3.0",
49
+ "jsdom": "^30.1.2",
49
50
  "react": "^19.3.0",
50
51
  "react-dom": "^19.3.0",
51
52
  "vitest": "^5.0.3"
@@ -66,6 +67,7 @@
66
67
  "scripts": {
67
68
  "build": "aui-build",
68
69
  "test": "vitest run",
70
+ "test:coverage": "vitest run --coverage --coverage.reporter=text --coverage.reporter=html --coverage.reporter=json-summary",
69
71
  "typecheck": "tsc --noEmit",
70
72
  "test:peer-react18": "vitest run --config vitest.peer-react18.config.ts",
71
73
  "test:types:peer-react18": "aui-react18-types"
@@ -1,6 +1,11 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { act } from "react";
4
+ import { createRoot } from "react-dom/client";
1
5
  import { renderToStaticMarkup } from "react-dom/server";
2
6
  import { describe, expect, it } from "vitest";
3
- import { AuiProvider, useAui } from "@assistant-ui/store";
7
+ import { useResource } from "@assistant-ui/tap";
8
+ import { AuiProvider, useAui, type ClientOutput } from "@assistant-ui/store";
4
9
  import * as SpanPrimitive from "../primitives/span";
5
10
  import { SpanResource, type SpanData } from "./SpanResource";
6
11
 
@@ -122,6 +127,59 @@ describe("SpanResource", () => {
122
127
  expect(spanIds).toEqual(["root", "earlier", "nested", "later"]);
123
128
  });
124
129
 
130
+ it("keeps sibling resources and flat list order when a span is toggled", () => {
131
+ const spans = [
132
+ createSpan("root", null),
133
+ createSpan("later", "root", 2),
134
+ createSpan("earlier", "root", 1),
135
+ createSpan("nested", "earlier", 3),
136
+ ];
137
+ let spanResource: ClientOutput<"span"> | undefined;
138
+ const container = document.createElement("div");
139
+ const root = createRoot(container);
140
+
141
+ const Fixture = () => {
142
+ spanResource = useResource(SpanResource({ spans }));
143
+ return null;
144
+ };
145
+
146
+ try {
147
+ act(() => root.render(<Fixture />));
148
+ const initial = spanResource!.getState().children;
149
+ expect(initial.map((span) => span.id)).toEqual([
150
+ "root",
151
+ "earlier",
152
+ "nested",
153
+ "later",
154
+ ]);
155
+
156
+ act(() => spanResource!.child({ key: "earlier" }).toggleCollapse());
157
+ const collapsed = spanResource!.getState().children;
158
+ expect(collapsed.map((span) => span.id)).toEqual([
159
+ "root",
160
+ "earlier",
161
+ "later",
162
+ ]);
163
+ expect(collapsed[0]).toBe(initial[0]);
164
+ expect(collapsed[2]).toBe(initial[3]);
165
+ expect(collapsed[1]?.isCollapsed).toBe(true);
166
+
167
+ act(() => spanResource!.child({ key: "earlier" }).toggleCollapse());
168
+ const expanded = spanResource!.getState().children;
169
+ expect(expanded.map((span) => span.id)).toEqual([
170
+ "root",
171
+ "earlier",
172
+ "nested",
173
+ "later",
174
+ ]);
175
+ expect(expanded[0]).toBe(initial[0]);
176
+ expect(expanded[3]).toBe(initial[3]);
177
+ expect(expanded[1]?.isCollapsed).toBe(false);
178
+ } finally {
179
+ act(() => root.unmount());
180
+ }
181
+ });
182
+
125
183
  it("renders deeply nested parent chains without overflowing the stack", () => {
126
184
  const spans = Array.from({ length: 20_000 }, (_, index) =>
127
185
  createSpan(`span-${index}`, index === 0 ? null : `span-${index - 1}`),
@@ -87,7 +87,7 @@ function normalizeSpanParents(
87
87
 
88
88
  function enrichSpans(rawSpans: SpanData[]): {
89
89
  allSpans: Map<string, SpanItemState>;
90
- parents: Map<string, string | null>;
90
+ children: Map<string | null, SpanItemState[]>;
91
91
  timeRange: { min: number; max: number };
92
92
  } {
93
93
  const spanMap = new Map<string, SpanData>();
@@ -132,17 +132,9 @@ function enrichSpans(rawSpans: SpanData[]): {
132
132
  if (max === -Infinity) max = Date.now();
133
133
  if (max === min) max = min + 100;
134
134
 
135
- return { allSpans, parents, timeRange: { min, max } };
136
- }
137
-
138
- function buildFlatList(
139
- allSpans: Map<string, SpanItemState>,
140
- parents: Map<string, string | null>,
141
- collapsedIds: Set<string>,
142
- ): SpanItemState[] {
143
135
  const children = new Map<string | null, SpanItemState[]>();
144
136
  for (const span of allSpans.values()) {
145
- const parent = parents.get(span.id) ?? null;
137
+ const parent = span.parentSpanId;
146
138
  let group = children.get(parent);
147
139
  if (!group) {
148
140
  group = [];
@@ -155,6 +147,13 @@ function buildFlatList(
155
147
  group.sort((a, b) => a.startedAt - b.startedAt);
156
148
  }
157
149
 
150
+ return { allSpans, children, timeRange: { min, max } };
151
+ }
152
+
153
+ function buildFlatList(
154
+ children: Map<string | null, SpanItemState[]>,
155
+ collapsedIds: Set<string>,
156
+ ): SpanItemState[] {
158
157
  const result: SpanItemState[] = [];
159
158
  const stack: SpanItemState[] = [];
160
159
  const roots = children.get(null) ?? [];
@@ -165,7 +164,7 @@ function buildFlatList(
165
164
  while (stack.length > 0) {
166
165
  const span = stack.pop()!;
167
166
  const isCollapsed = collapsedIds.has(span.id);
168
- result.push({ ...span, isCollapsed });
167
+ result.push(isCollapsed ? { ...span, isCollapsed } : span);
169
168
 
170
169
  if (!isCollapsed) {
171
170
  const kids = children.get(span.id) ?? [];
@@ -210,7 +209,7 @@ const useSpanResource = ({
210
209
  }: {
211
210
  spans: SpanData[];
212
211
  }): ClientOutput<"span"> => {
213
- const { allSpans, parents, timeRange } = useMemo(
212
+ const { allSpans, children, timeRange } = useMemo(
214
213
  () => enrichSpans(spans),
215
214
  [spans],
216
215
  );
@@ -220,8 +219,8 @@ const useSpanResource = ({
220
219
  );
221
220
 
222
221
  const visibleSpans = useMemo(
223
- () => buildFlatList(allSpans, parents, collapsedIds),
224
- [allSpans, parents, collapsedIds],
222
+ () => buildFlatList(children, collapsedIds),
223
+ [children, collapsedIds],
225
224
  );
226
225
 
227
226
  const toggleCollapse = (spanId: string) => {
@@ -245,6 +244,7 @@ const useSpanResource = ({
245
244
  timeRange,
246
245
  onToggleCollapse: toggleCollapse,
247
246
  }),
247
+ [allSpans, span.isCollapsed],
248
248
  ),
249
249
  ),
250
250
  );