Skip to content

Commit 76a5ac2

Browse files
authored
Realtime fixes: safari, timezones, and TIMED_OUT status (#1585)
* Fix realtime safari bug because of missing ReadableStream async iterable support Limit to only safari * Fix missing TIMED_OUT run status * When coercing realtime date strings, make sure they are set to UTC
1 parent 668b34d commit 76a5ac2

File tree

4 files changed

+96
-7
lines changed

4 files changed

+96
-7
lines changed

.changeset/poor-shirts-move.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@trigger.dev/core": patch
3+
---
4+
5+
Fix realtime safari bug because of missing ReadableStream async iterable support

.changeset/silent-trees-jump.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@trigger.dev/core": patch
3+
---
4+
5+
Fix issue with dates in realtime not reflecting the current timezone

packages/core/src/v3/apiClient/runStream.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,9 @@ function apiStatusFromRunStatus(status: string): RunStatus {
528528
case "EXPIRED": {
529529
return "EXPIRED";
530530
}
531+
case "TIMED_OUT": {
532+
return "TIMED_OUT";
533+
}
531534
default: {
532535
throw new Error(`Unknown status: ${status}`);
533536
}
@@ -541,3 +544,69 @@ function safeParseJSON(data: string): unknown {
541544
return data;
542545
}
543546
}
547+
548+
const isSafari = () => {
549+
// Check if we're in a browser environment
550+
if (
551+
typeof window !== "undefined" &&
552+
typeof navigator !== "undefined" &&
553+
typeof navigator.userAgent === "string"
554+
) {
555+
return (
556+
/^((?!chrome|android).)*safari/i.test(navigator.userAgent) ||
557+
/iPad|iPhone|iPod/.test(navigator.userAgent)
558+
);
559+
}
560+
// If we're not in a browser environment, return false
561+
return false;
562+
};
563+
564+
/**
565+
* A polyfill for `ReadableStream.protototype[Symbol.asyncIterator]`,
566+
* aligning as closely as possible to the specification.
567+
*
568+
* @see https://streams.spec.whatwg.org/#rs-asynciterator
569+
* @see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream#async_iteration
570+
*
571+
* This is needed for Safari: https://bugs.webkit.org/show_bug.cgi?id=194379
572+
*
573+
* From https://gist.github.com/MattiasBuelens/496fc1d37adb50a733edd43853f2f60e
574+
*
575+
*/
576+
577+
if (isSafari()) {
578+
// @ts-expect-error
579+
ReadableStream.prototype.values ??= function ({ preventCancel = false } = {}) {
580+
const reader = this.getReader();
581+
return {
582+
async next() {
583+
try {
584+
const result = await reader.read();
585+
if (result.done) {
586+
reader.releaseLock();
587+
}
588+
return result;
589+
} catch (e) {
590+
reader.releaseLock();
591+
throw e;
592+
}
593+
},
594+
async return(value: unknown) {
595+
if (!preventCancel) {
596+
const cancelPromise = reader.cancel(value);
597+
reader.releaseLock();
598+
await cancelPromise;
599+
} else {
600+
reader.releaseLock();
601+
}
602+
return { done: true, value };
603+
},
604+
[Symbol.asyncIterator]() {
605+
return this;
606+
},
607+
};
608+
};
609+
610+
// @ts-expect-error
611+
ReadableStream.prototype[Symbol.asyncIterator] ??= ReadableStream.prototype.values;
612+
}

packages/core/src/v3/schemas/api.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -682,16 +682,26 @@ export const UpdateMetadataResponseBody = z.object({
682682

683683
export type UpdateMetadataResponseBody = z.infer<typeof UpdateMetadataResponseBody>;
684684

685+
const RawShapeDate = z
686+
.string()
687+
.transform((val) => `${val}Z`)
688+
.pipe(z.coerce.date());
689+
690+
const RawOptionalShapeDate = z
691+
.string()
692+
.nullish()
693+
.transform((val) => (val ? new Date(`${val}Z`) : val));
694+
685695
export const SubscribeRunRawShape = z.object({
686696
id: z.string(),
687697
idempotencyKey: z.string().nullish(),
688-
createdAt: z.coerce.date(),
689-
updatedAt: z.coerce.date(),
690-
startedAt: z.coerce.date().nullish(),
691-
delayUntil: z.coerce.date().nullish(),
692-
queuedAt: z.coerce.date().nullish(),
693-
expiredAt: z.coerce.date().nullish(),
694-
completedAt: z.coerce.date().nullish(),
698+
createdAt: RawShapeDate,
699+
updatedAt: RawShapeDate,
700+
startedAt: RawOptionalShapeDate,
701+
delayUntil: RawOptionalShapeDate,
702+
queuedAt: RawOptionalShapeDate,
703+
expiredAt: RawOptionalShapeDate,
704+
completedAt: RawOptionalShapeDate,
695705
taskIdentifier: z.string(),
696706
friendlyId: z.string(),
697707
number: z.number(),

0 commit comments

Comments
 (0)