Skip to content

Commit e3cf456

Browse files
authored
v3: fix string and non-standard outputs (#983)
* Fixed string outputs missing and incorrectly formatted * Handle non-serializable outputs like a function * Add changeset
1 parent bf7827e commit e3cf456

File tree

11 files changed

+123
-64
lines changed

11 files changed

+123
-64
lines changed

.changeset/many-ligers-pump.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"trigger.dev": patch
3+
"@trigger.dev/core": patch
4+
---
5+
6+
Handle string and non-stringifiable outputs like functions

apps/webapp/app/entry.server.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,3 @@ export { apiRateLimiter } from "./services/apiRateLimit.server";
182182
export { socketIo } from "./v3/handleSocketIo.server";
183183
export { wss } from "./v3/handleWebsockets.server";
184184
export { registryProxy } from "./v3/registryProxy.server";
185-
186-
process.on("uncaughtException", (error, origin) => {
187-
logger.error("Uncaught Exception", { error, origin });
188-
});
189-
190-
process.on("unhandledRejection", (reason, promise) => {
191-
logger.error("Unhandled Rejection", { reason });
192-
});

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.spans.$spanParam/route.tsx

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -222,19 +222,25 @@ function PacketDisplay({
222222
dataType: string;
223223
title: string;
224224
}) {
225-
if (dataType === "application/store") {
226-
return (
227-
<div className="flex flex-col">
228-
<Paragraph variant="base/bright" className="w-full border-b border-grid-dimmed py-2.5">
229-
{title}
230-
</Paragraph>
231-
<LinkButton LeadingIcon={CloudArrowDownIcon} to={data} variant="tertiary/medium" download>
232-
Download
233-
</LinkButton>
234-
</div>
235-
);
236-
} else {
237-
return <CodeBlock rowTitle={title} code={data} maxLines={20} />;
225+
switch (dataType) {
226+
case "application/store": {
227+
return (
228+
<div className="flex flex-col">
229+
<Paragraph variant="base/bright" className="w-full border-b border-grid-dimmed py-2.5">
230+
{title}
231+
</Paragraph>
232+
<LinkButton LeadingIcon={CloudArrowDownIcon} to={data} variant="tertiary/medium" download>
233+
Download
234+
</LinkButton>
235+
</div>
236+
);
237+
}
238+
case "text/plain": {
239+
return <CodeBlock language="markdown" rowTitle={title} code={data} maxLines={20} />;
240+
}
241+
default: {
242+
return <CodeBlock language="json" rowTitle={title} code={data} maxLines={20} />;
243+
}
238244
}
239245
}
240246

apps/webapp/app/v3/eventRepository.server.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
SpanMessagingEvent,
1111
TaskEventStyle,
1212
correctErrorStackTrace,
13-
createPackageAttributesAsJson,
13+
createPacketAttributesAsJson,
1414
flattenAttributes,
1515
isExceptionSpanEvent,
1616
omit,
@@ -188,7 +188,7 @@ export class EventRepository {
188188
const event = events[0];
189189

190190
const output = options?.attributes.output
191-
? await createPackageAttributesAsJson(
191+
? await createPacketAttributesAsJson(
192192
options?.attributes.output,
193193
options?.attributes.outputType ?? "application/json"
194194
)
@@ -213,8 +213,9 @@ export class EventRepository {
213213
style: event.style as Attributes,
214214
output: output,
215215
outputType:
216-
options?.attributes.outputType === "application/store"
217-
? "application/store"
216+
options?.attributes.outputType === "application/store" ||
217+
options?.attributes.outputType === "text/plain"
218+
? options?.attributes.outputType
218219
: "application/json",
219220
payload: event.payload as Attributes,
220221
payloadType: event.payloadType,

apps/webapp/app/v3/services/completeAttempt.server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export class CompleteAttemptService extends BaseService {
105105
attributes: {
106106
isError: false,
107107
output:
108-
completion.outputType === "application/store"
108+
completion.outputType === "application/store" || completion.outputType === "text/plain"
109109
? completion.output
110110
: completion.output
111111
? (safeJsonParse(completion.output) as Attributes)

packages/cli-v3/src/commands/dev.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import * as packageJson from "../../package.json";
2626
import { CliApiClient } from "../apiClient";
2727
import { CommonCommandOptions, commonOptions, wrapCommandAction } from "../cli/common.js";
2828
import { bundleDependenciesPlugin, workerSetupImportConfigPlugin } from "../utilities/build";
29-
import { chalkGrey, chalkPurple, chalkWorker } from "../utilities/cliOutput";
29+
import { chalkError, chalkGrey, chalkPurple, chalkTask, chalkWorker } from "../utilities/cliOutput";
3030
import { readConfig } from "../utilities/configFiles";
3131
import { readJSONFile } from "../utilities/fileSystem";
3232
import { printDevBanner, printStandloneInitialBanner } from "../utilities/initialBanner.js";
@@ -77,9 +77,13 @@ export async function devCommand(dir: string, options: DevCommandOptions) {
7777

7878
if (!authorization.ok) {
7979
if (authorization.error === "fetch failed") {
80-
logger.error("Fetch failed. Platform down?");
80+
logger.log(
81+
`${chalkError(
82+
"X Error:"
83+
)} Connecting to the server failed. Please check your internet connection or contact [email protected] for help.`
84+
);
8185
} else {
82-
logger.error("You must login first. Use `trigger.dev login` to login.");
86+
logger.log(`${chalkError("X Error:")} You must login first. Use the \`login\` CLI command.`);
8387
}
8488
process.exitCode = 1;
8589
return;
@@ -701,13 +705,13 @@ function createDuplicateTaskIdOutputErrorMessage(
701705
.map((id) => {
702706
const tasks = taskResources.filter((task) => task.id === id);
703707

704-
return `id "${chalkPurple(id)}" was found in:\n${tasks
705-
.map((task) => `${task.filePath} -> ${task.exportName}`)
706-
.join("\n")}`;
708+
return `\n\n${chalkTask(id)} was found in:${tasks
709+
.map((task) => `\n${task.filePath} -> ${task.exportName}`)
710+
.join("")}`;
707711
})
708-
.join("\n\n");
712+
.join("");
709713

710-
return `Duplicate task ids detected:\n\n${duplicateTable}\n\n`;
714+
return `Duplicate ${chalkTask("task id")} detected:${duplicateTable}`;
711715
}
712716

713717
function gatherProcessEnv() {

packages/cli-v3/src/workers/dev/backgroundWorker.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,11 @@ import {
1818
formatDurationMilliseconds,
1919
workerToChildMessages,
2020
} from "@trigger.dev/core/v3";
21-
import chalk from "chalk";
2221
import dotenv from "dotenv";
2322
import { Evt } from "evt";
2423
import { ChildProcess, fork } from "node:child_process";
2524
import { dirname, resolve } from "node:path";
2625
import terminalLink from "terminal-link";
27-
import { safeDeleteFileSync } from "../../utilities/fileSystem.js";
28-
import { installPackages } from "../../utilities/installPackages.js";
29-
import { logger } from "../../utilities/logger.js";
30-
import { UncaughtExceptionError } from "../common/errors.js";
3126
import {
3227
chalkError,
3328
chalkGrey,
@@ -39,6 +34,10 @@ import {
3934
chalkWorker,
4035
prettyPrintDate,
4136
} from "../../utilities/cliOutput.js";
37+
import { safeDeleteFileSync } from "../../utilities/fileSystem.js";
38+
import { installPackages } from "../../utilities/installPackages.js";
39+
import { logger } from "../../utilities/logger.js";
40+
import { UncaughtExceptionError } from "../common/errors.js";
4241

4342
export type CurrentWorkers = BackgroundWorkerCoordinator["currentWorkers"];
4443
export class BackgroundWorkerCoordinator {

packages/core/src/v3/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export {
6464
stringifyIO,
6565
prettyPrintPacket,
6666
createPacketAttributes,
67-
createPackageAttributesAsJson,
67+
createPacketAttributesAsJson,
6868
conditionallyExportPacket,
6969
conditionallyImportPacket,
7070
packetRequiresOffloading,

packages/core/src/v3/utils/ioSerialization.ts

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,14 @@ export async function stringifyIO(value: any): Promise<IOPacket> {
4242
return { data: value, dataType: "text/plain" };
4343
}
4444

45-
const { stringify } = await loadSuperJSON();
45+
try {
46+
const { stringify } = await loadSuperJSON();
47+
const data = stringify(value);
4648

47-
return { data: stringify(value), dataType: "application/super+json" };
49+
return { data, dataType: "application/super+json" };
50+
} catch {
51+
return { dataType: "application/json" };
52+
}
4853
}
4954

5055
export async function conditionallyExportPacket(
@@ -193,9 +198,9 @@ export async function createPacketAttributes(
193198
packet: IOPacket,
194199
dataKey: string,
195200
dataTypeKey: string
196-
): Promise<Attributes> {
201+
): Promise<Attributes | undefined> {
197202
if (!packet.data) {
198-
return {};
203+
return;
199204
}
200205

201206
switch (packet.dataType) {
@@ -207,29 +212,38 @@ export async function createPacketAttributes(
207212
case "application/super+json":
208213
const { parse } = await loadSuperJSON();
209214

210-
const parsed = parse(packet.data) as any;
211-
const jsonified = JSON.parse(JSON.stringify(parsed, safeReplacer));
215+
if (typeof packet.data === "undefined" || packet.data === null) {
216+
return;
217+
}
218+
219+
try {
220+
const parsed = parse(packet.data) as any;
221+
const jsonified = JSON.parse(JSON.stringify(parsed, safeReplacer));
222+
223+
return {
224+
...flattenAttributes(jsonified, dataKey),
225+
[dataTypeKey]: "application/json",
226+
};
227+
} catch {
228+
return;
229+
}
212230

213-
return {
214-
...flattenAttributes(jsonified, dataKey),
215-
[dataTypeKey]: "application/json",
216-
};
217231
case "application/store":
218232
return {
219233
[dataKey]: packet.data,
220234
[dataTypeKey]: packet.dataType,
221235
};
222236
case "text/plain":
223237
return {
224-
[SemanticInternalAttributes.OUTPUT]: packet.data,
225-
[SemanticInternalAttributes.OUTPUT_TYPE]: packet.dataType,
238+
[dataKey]: packet.data,
239+
[dataTypeKey]: packet.dataType,
226240
};
227241
default:
228-
return {};
242+
return;
229243
}
230244
}
231245

232-
export async function createPackageAttributesAsJson(
246+
export async function createPacketAttributesAsJson(
233247
data: any,
234248
dataType: string
235249
): Promise<Attributes> {
@@ -250,7 +264,7 @@ export async function createPackageAttributesAsJson(
250264
const { deserialize } = await loadSuperJSON();
251265

252266
const deserialized = deserialize(data) as any;
253-
const jsonify = JSON.parse(JSON.stringify(deserialized, safeReplacer));
267+
const jsonify = safeJsonParse(JSON.stringify(deserialized, safeReplacer));
254268

255269
return imposeAttributeLimits(flattenAttributes(jsonify, undefined));
256270
case "application/store":
@@ -326,3 +340,11 @@ function getPacketExtension(outputType: string): string {
326340
async function loadSuperJSON(): Promise<typeof import("superjson")> {
327341
return await import("superjson");
328342
}
343+
344+
function safeJsonParse(value: string): any {
345+
try {
346+
return JSON.parse(value);
347+
} catch {
348+
return;
349+
}
350+
}

packages/core/src/v3/workers/taskExecutor.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,16 @@ export class TaskExecutor {
104104
this._tracer
105105
);
106106

107-
span.setAttributes(
108-
await createPacketAttributes(
109-
finalOutput,
110-
SemanticInternalAttributes.OUTPUT,
111-
SemanticInternalAttributes.OUTPUT_TYPE
112-
)
107+
const attributes = await createPacketAttributes(
108+
finalOutput,
109+
SemanticInternalAttributes.OUTPUT,
110+
SemanticInternalAttributes.OUTPUT_TYPE
113111
);
114112

113+
if (attributes) {
114+
span.setAttributes(attributes);
115+
}
116+
115117
return {
116118
ok: true,
117119
id: execution.attempt.id,

references/v3-catalog/src/trigger/superjson.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ export const superParentTask = task({
1717
logger.log(`typeof result.error = ${typeof result.error}`);
1818
logger.log(`typeof result.url = ${typeof result.url}`);
1919

20-
return {
21-
result,
22-
};
20+
return "## super-parent-task completed";
2321
},
2422
});
2523

@@ -120,6 +118,35 @@ export const superHugeOutputTask = task({
120118
},
121119
});
122120

121+
export const superStringTask = task({
122+
id: "super-string-parent-task",
123+
run: async () => {
124+
const result = await superStringChildTask.triggerAndWait({
125+
payload: {
126+
foo: "bar",
127+
},
128+
});
129+
130+
return result;
131+
},
132+
});
133+
134+
export const superStringChildTask = task({
135+
id: "super-string-child-task",
136+
run: async () => {
137+
return "## super-string-child-task completed";
138+
},
139+
});
140+
141+
export const superBadOutputTask = task({
142+
id: "super-bad-output-task",
143+
run: async () => {
144+
// Returning something that cannot be serialized
145+
146+
return () => {};
147+
},
148+
});
149+
123150
function createLargeObject(size: number, length: number) {
124151
return Array.from({ length }, (_, i) => [i.toString(), i.toString().padStart(size, "0")]).reduce(
125152
(acc, [key, value]) => {

0 commit comments

Comments
 (0)