forked from triggerdotdev/trigger.dev
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommon.server.ts
More file actions
177 lines (147 loc) · 4.57 KB
/
common.server.ts
File metadata and controls
177 lines (147 loc) · 4.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import { Attributes } from "@opentelemetry/api";
import { RandomIdGenerator } from "@opentelemetry/sdk-trace-base";
import { parseTraceparent } from "@trigger.dev/core/v3/isomorphic";
import { ExceptionEventProperties, SpanEvents, TaskRunError } from "@trigger.dev/core/v3/schemas";
import { unflattenAttributes } from "@trigger.dev/core/v3/utils/flattenAttributes";
import { createHash } from "node:crypto";
export function extractContextFromCarrier(carrier: Record<string, unknown>) {
const traceparent = carrier["traceparent"];
const tracestate = carrier["tracestate"];
if (typeof traceparent !== "string") {
return undefined;
}
return {
...carrier,
traceparent: parseTraceparent(traceparent),
tracestate,
};
}
export function getNowInNanoseconds(): bigint {
return BigInt(new Date().getTime()) * BigInt(1_000_000);
}
export function getDateFromNanoseconds(nanoseconds: bigint): Date {
return new Date(Number(nanoseconds) / 1_000_000);
}
export function calculateDurationFromStart(
startTime: bigint,
endTime: Date = new Date(),
minimumDuration?: number
) {
const $endtime = typeof endTime === "string" ? new Date(endTime) : endTime;
const duration = Number(BigInt($endtime.getTime()) * BigInt(1_000_000) - startTime);
if (minimumDuration && duration < minimumDuration) {
return minimumDuration;
}
return duration;
}
export function calculateDurationFromStartJsDate(startTime: Date, endTime: Date = new Date()) {
const $endtime = typeof endTime === "string" ? new Date(endTime) : endTime;
return ($endtime.getTime() - startTime.getTime()) * 1_000_000;
}
export function convertDateToNanoseconds(date: Date): bigint {
return BigInt(date.getTime()) * BigInt(1_000_000);
}
/**
* Returns a deterministically random 8-byte span ID formatted/encoded as a 16 lowercase hex
* characters corresponding to 64 bits, based on the trace ID and seed.
*/
export function generateDeterministicSpanId(traceId: string, seed: string) {
const hash = createHash("sha1");
hash.update(traceId);
hash.update(seed);
const buffer = hash.digest();
let hexString = "";
for (let i = 0; i < 8; i++) {
const val = buffer.readUInt8(i);
const str = val.toString(16).padStart(2, "0");
hexString += str;
}
return hexString;
}
const randomIdGenerator = new RandomIdGenerator();
export function generateTraceId() {
return randomIdGenerator.generateTraceId();
}
export function generateSpanId() {
return randomIdGenerator.generateSpanId();
}
export function stripAttributePrefix(attributes: Attributes, prefix: string) {
const result: Attributes = {};
for (const [key, value] of Object.entries(attributes)) {
if (key.startsWith(prefix)) {
result[key.slice(prefix.length + 1)] = value;
} else {
result[key] = value;
}
}
return result;
}
export function parseEventsField(events: unknown): SpanEvents {
if (!events) return [];
if (!Array.isArray(events)) return [];
const unsafe = events
? (events as any[]).map((e) => ({
...e,
properties: unflattenAttributes(e.properties as Attributes),
}))
: undefined;
return unsafe as SpanEvents;
}
export function createExceptionPropertiesFromError(error: TaskRunError): ExceptionEventProperties {
switch (error.type) {
case "BUILT_IN_ERROR": {
return {
type: error.name,
message: error.message,
stacktrace: error.stackTrace,
};
}
case "CUSTOM_ERROR": {
return {
type: "Error",
message: error.raw,
};
}
case "INTERNAL_ERROR": {
return {
type: "Internal error",
message: [error.code, error.message].filter(Boolean).join(": "),
stacktrace: error.stackTrace,
};
}
case "STRING_ERROR": {
return {
type: "Error",
message: error.raw,
};
}
}
}
// Removes internal/private attribute keys from span properties.
// Filters: "$" prefixed keys (private metadata) and "ctx." prefixed keys (Trigger.dev run context)
export function removePrivateProperties(
attributes: Attributes | undefined | null
): Attributes | undefined {
if (!attributes) {
return undefined;
}
const result: Attributes = {};
for (const [key, value] of Object.entries(attributes)) {
if (key.startsWith("$") || key.startsWith("ctx.")) {
continue;
}
result[key] = value;
}
if (Object.keys(result).length === 0) {
return undefined;
}
return result;
}
export function isEmptyObject(obj: object) {
for (var prop in obj) {
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
return false;
}
}
return true;
}