@proj-airi/trace-kit 0.0.0-stage → 0.15.3

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.
@@ -0,0 +1,118 @@
1
+ //#region src/core/series-ring.ts
2
+ /** The retention of the collector rings, and the default retention of a live timeline. */
3
+ const DEFAULT_SERIES_RETENTION = {
4
+ maxAgeMs: 18e5,
5
+ fullRateMs: 3e5
6
+ };
7
+ /**
8
+ * A columnar ring of one series: point times on the trace clock and their values.
9
+ *
10
+ * Retention: points newer than `fullRateMs` keep the full rate. Older points keep the minimum and the maximum of each
11
+ * second, so a long view still shows peaks. Points older than `maxAgeMs` go.
12
+ */
13
+ var SeriesRing = class {
14
+ maxAgeMs;
15
+ fullRateMs;
16
+ times = /* @__PURE__ */ new Float64Array(256);
17
+ values = /* @__PURE__ */ new Float32Array(256);
18
+ length = 0;
19
+ /** Points before this index are already reduced to one second buckets. */
20
+ compactedUntil = 0;
21
+ changes = 0;
22
+ constructor(maxAgeMs, fullRateMs) {
23
+ this.maxAgeMs = maxAgeMs;
24
+ this.fullRateMs = fullRateMs;
25
+ }
26
+ get size() {
27
+ return this.length;
28
+ }
29
+ /**
30
+ * Changes when points that were already in the ring change: a compaction, an expiry, or a late point inserted
31
+ * before newer points. A reader that copies points incrementally starts again after a change.
32
+ */
33
+ get revision() {
34
+ return this.changes;
35
+ }
36
+ /** The point times, in time order. The view is valid until the next append or compaction. */
37
+ timesView() {
38
+ return this.times.subarray(0, this.length);
39
+ }
40
+ /** The point values, in the order of `timesView`. The view is valid until the next append or compaction. */
41
+ valuesView() {
42
+ return this.values.subarray(0, this.length);
43
+ }
44
+ append(time, value) {
45
+ if (this.length === this.times.length) this.grow();
46
+ let index = this.length;
47
+ while (index > 0 && this.times[index - 1] > time) index--;
48
+ if (index < this.length) {
49
+ this.changes++;
50
+ this.times.copyWithin(index + 1, index, this.length);
51
+ this.values.copyWithin(index + 1, index, this.length);
52
+ }
53
+ this.times[index] = time;
54
+ this.values[index] = value;
55
+ this.length++;
56
+ if (index < this.compactedUntil) this.compactedUntil++;
57
+ }
58
+ /** Reduces old points and drops expired points. `now` is on the trace clock. */
59
+ compact(now) {
60
+ const expiry = now - this.maxAgeMs;
61
+ let start = 0;
62
+ while (start < this.length && this.times[start] < expiry) start++;
63
+ if (start > 0) this.shift(start);
64
+ const fullRateStart = now - this.fullRateMs;
65
+ let end = this.compactedUntil;
66
+ while (end < this.length && this.times[end] < fullRateStart) end++;
67
+ if (end === this.compactedUntil) return;
68
+ const reducedTimes = [];
69
+ const reducedValues = [];
70
+ let index = this.compactedUntil;
71
+ while (index < end) {
72
+ const bucket = Math.floor(this.times[index] / 1e3);
73
+ let minIndex = index;
74
+ let maxIndex = index;
75
+ while (index < end && Math.floor(this.times[index] / 1e3) === bucket) {
76
+ if (this.values[index] < this.values[minIndex]) minIndex = index;
77
+ if (this.values[index] > this.values[maxIndex]) maxIndex = index;
78
+ index++;
79
+ }
80
+ for (const kept of minIndex === maxIndex ? [minIndex] : [Math.min(minIndex, maxIndex), Math.max(minIndex, maxIndex)]) {
81
+ reducedTimes.push(this.times[kept]);
82
+ reducedValues.push(this.values[kept]);
83
+ }
84
+ }
85
+ const removed = end - this.compactedUntil - reducedTimes.length;
86
+ if (removed > 0) this.changes++;
87
+ this.times.set(reducedTimes, this.compactedUntil);
88
+ this.values.set(reducedValues, this.compactedUntil);
89
+ this.times.copyWithin(this.compactedUntil + reducedTimes.length, end, this.length);
90
+ this.values.copyWithin(this.compactedUntil + reducedTimes.length, end, this.length);
91
+ this.length -= removed;
92
+ this.compactedUntil += reducedTimes.length;
93
+ }
94
+ /** A copy of the points, in time order. */
95
+ read() {
96
+ return {
97
+ times: Array.from(this.times.subarray(0, this.length)),
98
+ values: Array.from(this.values.subarray(0, this.length))
99
+ };
100
+ }
101
+ shift(count) {
102
+ this.changes++;
103
+ this.times.copyWithin(0, count, this.length);
104
+ this.values.copyWithin(0, count, this.length);
105
+ this.length -= count;
106
+ this.compactedUntil = Math.max(0, this.compactedUntil - count);
107
+ }
108
+ grow() {
109
+ const times = new Float64Array(this.times.length * 2);
110
+ const values = new Float32Array(this.values.length * 2);
111
+ times.set(this.times);
112
+ values.set(this.values);
113
+ this.times = times;
114
+ this.values = values;
115
+ }
116
+ };
117
+ //#endregion
118
+ export { SeriesRing as n, DEFAULT_SERIES_RETENTION as t };
@@ -0,0 +1,49 @@
1
+ //#region src/core/series-ring.d.ts
2
+ /** How long a series keeps its points. */
3
+ interface SeriesRetention {
4
+ /** Points older than this go. */
5
+ maxAgeMs: number;
6
+ /** Points newer than this keep the full rate. Older points keep the minimum and the maximum of each second. */
7
+ fullRateMs: number;
8
+ }
9
+ /** The retention of the collector rings, and the default retention of a live timeline. */
10
+ declare const DEFAULT_SERIES_RETENTION: Readonly<SeriesRetention>;
11
+ /**
12
+ * A columnar ring of one series: point times on the trace clock and their values.
13
+ *
14
+ * Retention: points newer than `fullRateMs` keep the full rate. Older points keep the minimum and the maximum of each
15
+ * second, so a long view still shows peaks. Points older than `maxAgeMs` go.
16
+ */
17
+ declare class SeriesRing {
18
+ private readonly maxAgeMs;
19
+ private readonly fullRateMs;
20
+ private times;
21
+ private values;
22
+ private length;
23
+ /** Points before this index are already reduced to one second buckets. */
24
+ private compactedUntil;
25
+ private changes;
26
+ constructor(maxAgeMs: number, fullRateMs: number);
27
+ get size(): number;
28
+ /**
29
+ * Changes when points that were already in the ring change: a compaction, an expiry, or a late point inserted
30
+ * before newer points. A reader that copies points incrementally starts again after a change.
31
+ */
32
+ get revision(): number;
33
+ /** The point times, in time order. The view is valid until the next append or compaction. */
34
+ timesView(): Float64Array;
35
+ /** The point values, in the order of `timesView`. The view is valid until the next append or compaction. */
36
+ valuesView(): Float32Array;
37
+ append(time: number, value: number): void;
38
+ /** Reduces old points and drops expired points. `now` is on the trace clock. */
39
+ compact(now: number): void;
40
+ /** A copy of the points, in time order. */
41
+ read(): {
42
+ times: number[];
43
+ values: number[];
44
+ };
45
+ private shift;
46
+ private grow;
47
+ }
48
+ //#endregion
49
+ export { SeriesRetention as n, SeriesRing as r, DEFAULT_SERIES_RETENTION as t };
@@ -0,0 +1,501 @@
1
+ import { G as definitionRecordsOf } from "./conventions-DLiC8rkL.mjs";
2
+ import { array, boolean, check, finite, integer, literal, maxLength, minValue, nonEmpty, number, object, optional, picklist, pipe, record, regex, string, union, unknown, variant } from "valibot";
3
+ //#region src/core/records.ts
4
+ /**
5
+ * Version of the record format. A reader upcasts an older minor format and rejects a newer major format.
6
+ * `source.started.format` carries it.
7
+ */
8
+ const TRACE_FORMAT = 1;
9
+ const SYSTEM_TYPES = /* @__PURE__ */ new Set([
10
+ "source.started",
11
+ "source.stopped",
12
+ "source.lost",
13
+ "clock.snapshot",
14
+ "clock.discontinuity",
15
+ "stream.started",
16
+ "stream.stopped",
17
+ "definition"
18
+ ]);
19
+ function isSystemRecord(record) {
20
+ return SYSTEM_TYPES.has(record.type);
21
+ }
22
+ function isTrackedRecord(record) {
23
+ return "track" in record && typeof record.track === "string";
24
+ }
25
+ //#endregion
26
+ //#region src/core/schema.ts
27
+ const nonEmptyString = pipe(string(), nonEmpty());
28
+ /**
29
+ * The most points in one `sample.chunk`, over all its series. A chunk holds about one second, so this is above 4 kHz
30
+ * for one series. Series rings sit outside the byte limit of the buffer, so an untrusted chunk must be bounded here.
31
+ */
32
+ const MAX_CHUNK_POINTS = 4096;
33
+ const finiteNumber = pipe(number(), finite());
34
+ /** A source or an epoch. Keys join them with `/`, so a `/` inside would make two keys equal. */
35
+ const keyPart = pipe(string(), nonEmpty(), regex(/^[^/]+$/, "A source or an epoch must not contain \"/\"."));
36
+ const assetRef = object({
37
+ $asset: nonEmptyString,
38
+ mediaType: nonEmptyString,
39
+ bytes: pipe(number(), integer(), minValue(0))
40
+ });
41
+ const value = union([
42
+ string(),
43
+ finiteNumber,
44
+ boolean(),
45
+ array(string()),
46
+ array(finiteNumber),
47
+ assetRef
48
+ ]);
49
+ const ids = record(string(), union([string(), finiteNumber]));
50
+ const ref = object({
51
+ source: keyPart,
52
+ epoch: keyPart,
53
+ seq: pipe(number(), integer(), minValue(0))
54
+ });
55
+ const link = object({
56
+ to: union([
57
+ ref,
58
+ object({ spanId: nonEmptyString }),
59
+ object({
60
+ key: nonEmptyString,
61
+ id: union([string(), finiteNumber])
62
+ })
63
+ ]),
64
+ rel: nonEmptyString
65
+ });
66
+ const status = picklist([
67
+ "ok",
68
+ "error",
69
+ "cancelled"
70
+ ]);
71
+ const untracked = {
72
+ source: keyPart,
73
+ epoch: keyPart,
74
+ seq: pipe(number(), integer(), minValue(0)),
75
+ ts: finiteNumber,
76
+ clock: nonEmptyString,
77
+ observedTs: optional(finiteNumber),
78
+ ids: optional(ids),
79
+ attrs: optional(record(string(), value))
80
+ };
81
+ const tracked = {
82
+ ...untracked,
83
+ track: nonEmptyString,
84
+ links: optional(array(link)),
85
+ evidence: optional(picklist(["reported", "observed"]))
86
+ };
87
+ /**
88
+ * Validates one trace record at a trust boundary, such as IPC from a window or `trace:emit` from a plugin.
89
+ * Producers inside one process do not validate their own records.
90
+ */
91
+ const traceRecordSchema = variant("type", [
92
+ object({
93
+ ...untracked,
94
+ type: literal("source.started"),
95
+ format: pipe(number(), integer()),
96
+ runtime: string(),
97
+ profiles: array(object({
98
+ key: nonEmptyString,
99
+ version: string()
100
+ }))
101
+ }),
102
+ object({
103
+ ...untracked,
104
+ type: literal("source.stopped")
105
+ }),
106
+ object({
107
+ ...untracked,
108
+ type: literal("source.lost"),
109
+ of: object({
110
+ source: keyPart,
111
+ epoch: keyPart,
112
+ lastSeq: number()
113
+ }),
114
+ reason: picklist([
115
+ "ipc-closed",
116
+ "heartbeat-timeout",
117
+ "epoch-replaced"
118
+ ])
119
+ }),
120
+ object({
121
+ ...untracked,
122
+ type: literal("clock.snapshot"),
123
+ values: array(object({
124
+ clock: nonEmptyString,
125
+ value: finiteNumber
126
+ })),
127
+ uncertaintyMs: pipe(finiteNumber, minValue(0))
128
+ }),
129
+ object({
130
+ ...untracked,
131
+ type: literal("clock.discontinuity"),
132
+ of: nonEmptyString,
133
+ reason: string()
134
+ }),
135
+ object({
136
+ ...untracked,
137
+ type: literal("stream.started"),
138
+ streamId: nonEmptyString,
139
+ mediaType: nonEmptyString,
140
+ rate: optional(finiteNumber),
141
+ channels: optional(pipe(number(), integer())),
142
+ uri: optional(string())
143
+ }),
144
+ object({
145
+ ...untracked,
146
+ type: literal("stream.stopped"),
147
+ streamId: nonEmptyString
148
+ }),
149
+ object({
150
+ ...tracked,
151
+ type: literal("span.begin"),
152
+ spanId: nonEmptyString,
153
+ parentSpanId: optional(nonEmptyString),
154
+ name: string(),
155
+ deadlineMs: optional(pipe(finiteNumber, minValue(0)))
156
+ }),
157
+ object({
158
+ ...tracked,
159
+ type: literal("span.update"),
160
+ spanId: nonEmptyString
161
+ }),
162
+ object({
163
+ ...tracked,
164
+ type: literal("span.end"),
165
+ spanId: nonEmptyString,
166
+ status,
167
+ reason: optional(string()),
168
+ message: optional(string())
169
+ }),
170
+ object({
171
+ ...tracked,
172
+ type: literal("span.complete"),
173
+ spanId: nonEmptyString,
174
+ parentSpanId: optional(nonEmptyString),
175
+ name: string(),
176
+ durationMs: pipe(finiteNumber, minValue(0)),
177
+ status,
178
+ reason: optional(string())
179
+ }),
180
+ object({
181
+ ...tracked,
182
+ type: literal("instant"),
183
+ name: string(),
184
+ spanId: optional(nonEmptyString)
185
+ }),
186
+ object({
187
+ ...tracked,
188
+ type: literal("state"),
189
+ value: string(),
190
+ from: optional(string())
191
+ }),
192
+ object({
193
+ ...tracked,
194
+ type: literal("sample"),
195
+ values: record(string(), finiteNumber)
196
+ }),
197
+ object({
198
+ ...tracked,
199
+ type: literal("sample.chunk"),
200
+ intervalMs: optional(pipe(finiteNumber, minValue(0))),
201
+ offsetsMs: optional(pipe(array(finiteNumber), maxLength(MAX_CHUNK_POINTS))),
202
+ values: pipe(record(string(), array(finiteNumber)), check((values) => Object.values(values).reduce((count, list) => count + list.length, 0) <= MAX_CHUNK_POINTS, `A sample chunk holds at most ${MAX_CHUNK_POINTS} points.`))
203
+ }),
204
+ object({
205
+ ...tracked,
206
+ type: literal("log"),
207
+ level: picklist([
208
+ "debug",
209
+ "info",
210
+ "warn",
211
+ "error"
212
+ ]),
213
+ message: string()
214
+ }),
215
+ object({
216
+ ...untracked,
217
+ type: literal("definition"),
218
+ kind: picklist([
219
+ "track",
220
+ "attribute",
221
+ "id-key",
222
+ "link",
223
+ "end-reason",
224
+ "clock",
225
+ "metric",
226
+ "score",
227
+ "pipeline",
228
+ "preset",
229
+ "profile"
230
+ ]),
231
+ key: nonEmptyString,
232
+ data: unknown()
233
+ }),
234
+ object({
235
+ ...untracked,
236
+ type: literal("annotation"),
237
+ target: object({
238
+ ref: optional(ref),
239
+ spanId: optional(nonEmptyString),
240
+ ids: optional(ids)
241
+ }),
242
+ name: nonEmptyString,
243
+ value: optional(value),
244
+ label: optional(string()),
245
+ explanation: optional(string()),
246
+ annotator: object({
247
+ kind: picklist([
248
+ "rule",
249
+ "model",
250
+ "human"
251
+ ]),
252
+ id: optional(string()),
253
+ model: optional(string()),
254
+ version: optional(string())
255
+ })
256
+ })
257
+ ]);
258
+ //#endregion
259
+ //#region src/core/sink.ts
260
+ const noopTraceSink = {
261
+ emit: () => {},
262
+ wants: () => false,
263
+ now: () => performance.now()
264
+ };
265
+ /**
266
+ * Sends each record to several sinks. The error of one sink goes to `onError` and does not stop the others.
267
+ * `wants` is true when any sink wants the track. `now` comes from the first sink.
268
+ */
269
+ function fanOutTraceSink(sinks, onError) {
270
+ return {
271
+ emit: (record) => {
272
+ for (const sink of sinks) try {
273
+ sink.emit(record);
274
+ } catch (error) {
275
+ onError(error);
276
+ }
277
+ },
278
+ wants: (track) => sinks.some((sink) => sink.wants(track)),
279
+ now: () => (sinks[0] ?? noopTraceSink).now()
280
+ };
281
+ }
282
+ /**
283
+ * One run of a producer: it stamps `source`, `epoch`, `seq`, and the default clock on each record.
284
+ *
285
+ * Lifecycle: the constructor writes `source.started` and the definitions of its profiles. `stop` writes
286
+ * `source.stopped` and ignores later records. A reload creates a new instance, which is a new epoch.
287
+ *
288
+ * `wants` reads the track set that {@link TraceSource.setWanted} received from the collector. Until the collector
289
+ * answers, no costly series is wanted.
290
+ */
291
+ var TraceSource = class {
292
+ source;
293
+ epoch;
294
+ clock;
295
+ seq = 0;
296
+ stopped = false;
297
+ wanted = /* @__PURE__ */ new Set();
298
+ wantsAll = false;
299
+ sendRecord;
300
+ now;
301
+ constructor(options) {
302
+ this.source = options.source;
303
+ this.clock = options.clock;
304
+ this.epoch = (options.createEpoch ?? (() => crypto.randomUUID()))();
305
+ this.sendRecord = options.send;
306
+ this.now = options.now ?? (() => performance.now());
307
+ const ts = this.now();
308
+ const profiles = options.profiles ?? [];
309
+ this.emit({
310
+ type: "source.started",
311
+ ts,
312
+ format: 1,
313
+ runtime: options.runtime,
314
+ profiles: profiles.map((profile) => ({
315
+ key: profile.key,
316
+ version: profile.version
317
+ })),
318
+ ...options.attrs ? { attrs: options.attrs } : {}
319
+ });
320
+ for (const profile of profiles) this.announce(profile);
321
+ }
322
+ /** Announces the definitions of a profile, for example a plugin profile that loads later. */
323
+ announce(profile) {
324
+ for (const record of definitionRecordsOf(profile, this.now())) this.emit(record);
325
+ }
326
+ emit = (input) => {
327
+ if (this.stopped) return;
328
+ this.sendRecord({
329
+ ...input,
330
+ clock: input.clock ?? this.clock,
331
+ source: this.source,
332
+ epoch: this.epoch,
333
+ seq: this.seq++
334
+ });
335
+ };
336
+ wants = (track) => {
337
+ return this.wantsAll || this.wanted.has(track);
338
+ };
339
+ /** Sets the track paths that readers show. `'*'` wants every track. */
340
+ setWanted(tracks) {
341
+ this.wantsAll = tracks === "*";
342
+ this.wanted = tracks === "*" ? /* @__PURE__ */ new Set() : new Set(tracks);
343
+ }
344
+ stop() {
345
+ if (this.stopped) return;
346
+ this.emit({
347
+ type: "source.stopped",
348
+ ts: this.now()
349
+ });
350
+ this.stopped = true;
351
+ }
352
+ };
353
+ /** A random 16 hex digit span ID, the size of a W3C trace context span ID. */
354
+ function createSpanId() {
355
+ const bytes = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(8));
356
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
357
+ }
358
+ /**
359
+ * An open span of one producer. It writes `span.begin` at once, so readers see open work.
360
+ *
361
+ * Lifecycle: `end` writes `span.end` once. Later calls of `update`, `instant`, and `end` do nothing, so a cleanup
362
+ * path can end a span that a success path already ended.
363
+ */
364
+ var TraceSpan = class {
365
+ sink;
366
+ spanId;
367
+ track;
368
+ ids;
369
+ ended = false;
370
+ constructor(sink, options) {
371
+ this.sink = sink;
372
+ this.spanId = options.spanId ?? createSpanId();
373
+ this.track = options.track;
374
+ this.ids = options.ids;
375
+ const parentSpanId = typeof options.parent === "string" ? options.parent : options.parent?.spanId;
376
+ sink.emit({
377
+ type: "span.begin",
378
+ ts: options.ts ?? sink.now(),
379
+ track: options.track,
380
+ spanId: this.spanId,
381
+ name: options.name,
382
+ ...parentSpanId ? { parentSpanId } : {},
383
+ ...options.ids ? { ids: options.ids } : {},
384
+ ...options.attrs ? { attrs: options.attrs } : {},
385
+ ...options.links ? { links: options.links } : {},
386
+ ...options.deadlineMs !== void 0 ? { deadlineMs: options.deadlineMs } : {}
387
+ });
388
+ }
389
+ get isEnded() {
390
+ return this.ended;
391
+ }
392
+ update(attrs) {
393
+ if (this.ended) return;
394
+ this.sink.emit({
395
+ type: "span.update",
396
+ ts: this.sink.now(),
397
+ track: this.track,
398
+ spanId: this.spanId,
399
+ attrs,
400
+ ...this.idFields()
401
+ });
402
+ }
403
+ instant(name, attrs, ts) {
404
+ if (this.ended) return;
405
+ this.sink.emit({
406
+ type: "instant",
407
+ ts: ts ?? this.sink.now(),
408
+ track: this.track,
409
+ spanId: this.spanId,
410
+ name,
411
+ ...attrs ? { attrs } : {},
412
+ ...this.idFields()
413
+ });
414
+ }
415
+ end(options = {}) {
416
+ if (this.ended) return;
417
+ this.ended = true;
418
+ this.sink.emit({
419
+ type: "span.end",
420
+ ts: options.ts ?? this.sink.now(),
421
+ track: this.track,
422
+ spanId: this.spanId,
423
+ status: options.status ?? "ok",
424
+ ...options.reason ? { reason: options.reason } : {},
425
+ ...options.message ? { message: options.message } : {},
426
+ ...options.attrs ? { attrs: options.attrs } : {},
427
+ ...options.links ? { links: options.links } : {},
428
+ ...this.idFields()
429
+ });
430
+ }
431
+ idFields() {
432
+ return this.ids ? { ids: this.ids } : {};
433
+ }
434
+ };
435
+ /** Writes `span.begin` on `sink` and returns the open span. */
436
+ function beginSpan(sink, options) {
437
+ return new TraceSpan(sink, options);
438
+ }
439
+ /**
440
+ * Writes a frequent series, such as an audio level or a VAD probability, as `sample.chunk` records.
441
+ *
442
+ * Samples wait in memory until the chunk reaches `chunkMs`, then go out as one record with offsets from its first
443
+ * sample. While the sink does not want the track, `push` drops the sample, so a series that no one reads costs nothing.
444
+ * Call `flush` when the series pauses, for example when an input ends, so its last samples show at once.
445
+ */
446
+ var SeriesWriter = class {
447
+ sink;
448
+ options;
449
+ chunkMs;
450
+ start;
451
+ offsets = [];
452
+ values = /* @__PURE__ */ new Map();
453
+ constructor(sink, options) {
454
+ this.sink = sink;
455
+ this.options = options;
456
+ this.chunkMs = options.chunkMs ?? 1e3;
457
+ }
458
+ /** Whether a reader wants the track now. A producer can skip costly work, such as decoding, while it is false. */
459
+ wanted() {
460
+ return this.sink().wants(this.options.track);
461
+ }
462
+ push(values, ts) {
463
+ const sink = this.sink();
464
+ if (!sink.wants(this.options.track)) {
465
+ this.offsets.length = 0;
466
+ this.values.clear();
467
+ this.start = void 0;
468
+ return;
469
+ }
470
+ const time = ts ?? sink.now();
471
+ const keys = Object.keys(values);
472
+ const sameKeys = keys.length === this.values.size && keys.every((key) => this.values.has(key));
473
+ if (this.start !== void 0 && (time - this.start >= this.chunkMs || !sameKeys)) this.flush();
474
+ this.start ??= time;
475
+ this.offsets.push(time - this.start);
476
+ for (const key of keys) {
477
+ let column = this.values.get(key);
478
+ if (!column) {
479
+ column = [];
480
+ this.values.set(key, column);
481
+ }
482
+ column.push(values[key]);
483
+ }
484
+ }
485
+ flush() {
486
+ if (this.start === void 0 || this.offsets.length === 0) return;
487
+ this.sink().emit({
488
+ type: "sample.chunk",
489
+ ts: this.start,
490
+ track: this.options.track,
491
+ ...this.options.ids ? { ids: this.options.ids } : {},
492
+ offsetsMs: [...this.offsets],
493
+ values: Object.fromEntries([...this.values].map(([key, column]) => [key, [...column]]))
494
+ });
495
+ this.offsets.length = 0;
496
+ this.values.clear();
497
+ this.start = void 0;
498
+ }
499
+ };
500
+ //#endregion
501
+ export { createSpanId as a, MAX_CHUNK_POINTS as c, isSystemRecord as d, isTrackedRecord as f, beginSpan as i, traceRecordSchema as l, TraceSource as n, fanOutTraceSink as o, TraceSpan as r, noopTraceSink as s, SeriesWriter as t, TRACE_FORMAT as u };