Skip to content

Commit c8ca959

Browse files
committed
Ignore undefined type metadata when overriding superjson payloads
Addresses #1968
1 parent 222b108 commit c8ca959

3 files changed

Lines changed: 54 additions & 1 deletion

File tree

packages/core/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@
188188
"execa": "^8.0.1",
189189
"humanize-duration": "^3.27.3",
190190
"jose": "^5.4.0",
191+
"lodash.get": "^4.4.2",
191192
"nanoid": "3.3.8",
192193
"prom-client": "^15.1.0",
193194
"socket.io": "4.7.4",
@@ -206,6 +207,7 @@
206207
"@epic-web/test-server": "^0.1.0",
207208
"@trigger.dev/database": "workspace:*",
208209
"@types/humanize-duration": "^3.27.1",
210+
"@types/lodash.get": "^4.4.9",
209211
"@types/readable-stream": "^4.0.14",
210212
"ai": "^3.4.33",
211213
"defu": "^6.1.4",

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { SemanticInternalAttributes } from "../semanticInternalAttributes.js";
1212
import { TriggerTracer } from "../tracer.js";
1313
import { zodfetch } from "../zodfetch.js";
1414
import { flattenAttributes } from "./flattenAttributes.js";
15+
import get from "lodash.get";
1516

1617
export type IOPacket = {
1718
data?: string | undefined;
@@ -505,13 +506,46 @@ function safeJsonParse(value: string): any {
505506
}
506507
}
507508

509+
/**
510+
* Replaces the data in a SuperJSON-serialized string with new payload data while preserving
511+
* the original type metadata (Dates, BigInts, Sets, Maps, etc.).
512+
*
513+
* It is primarily useful for our run replay functionality where we want to preserve the original
514+
* type metadata for the new payload.
515+
*
516+
* Note that `undefined` type metadata is ignored when the corresponding field is overriden in the
517+
* new payload, i.e., fields which were previously undefined in the original payload are restored into
518+
* the primitive type they have in the new payload, instead of `undefined`.
519+
* This is a workaround for https://github.com/triggerdotdev/trigger.dev/issues/1968.
520+
*
521+
* @param original - A SuperJSON-serialized string containing the original data with type metadata
522+
* @param newPayload - A JSON string containing the new data to replace the original payload
523+
* @returns The deserialized object with new data but original type metadata preserved
524+
*
525+
* @throws {Error} If the newPayload is not valid JSON
526+
*/
508527
export async function replaceSuperJsonPayload(original: string, newPayload: string) {
509528
const superjson = await loadSuperJSON();
510529
const originalObject = superjson.parse(original);
530+
const newPayloadObject = JSON.parse(newPayload);
511531
const { meta } = superjson.serialize(originalObject);
512532

533+
if (meta?.values) {
534+
const originalUndefinedKeys = Object.entries(meta.values)
535+
.filter(([, value]) => Array.isArray(value) && value.at(0) === "undefined")
536+
.map(([key]) => key);
537+
538+
const overridenUndefinedKeys = originalUndefinedKeys.filter(
539+
(key) => get(newPayloadObject, key) !== undefined
540+
);
541+
542+
overridenUndefinedKeys.forEach((key) => {
543+
delete (meta.values as Record<string, any>)[key];
544+
});
545+
}
546+
513547
const newSuperJson = {
514-
json: JSON.parse(newPayload) as any,
548+
json: newPayloadObject,
515549
meta,
516550
};
517551

pnpm-lock.yaml

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)