Using ElectricSQL for the run page data
This commit is contained in:
@@ -31,6 +31,7 @@ const EnvironmentSchema = z.object({
|
||||
REMIX_APP_PORT: z.string().optional(),
|
||||
LOGIN_ORIGIN: z.string().default("http://localhost:3030"),
|
||||
APP_ORIGIN: z.string().default("http://localhost:3030"),
|
||||
ELECTRIC_ORIGIN: z.string().default("http://localhost:3060"),
|
||||
APP_ENV: z.string().default(process.env.NODE_ENV),
|
||||
SERVICE_NAME: z.string().default("trigger.dev webapp"),
|
||||
SECRET_STORE: SecretStoreOptionsSchema.default("DATABASE"),
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { useShape } from "@electric-sql/react";
|
||||
import { createTraceTreeFromEvents, prepareTrace } from "~/utils/taskEvent";
|
||||
import { QueriedEvent } from "~/v3/eventRepository.server";
|
||||
|
||||
type TraceInput = {
|
||||
origin: string;
|
||||
traceId: string;
|
||||
spanId: string;
|
||||
};
|
||||
|
||||
export type Trace = ReturnType<typeof createTraceTreeFromEvents>;
|
||||
export type TraceEvent = NonNullable<Trace["events"][number]>;
|
||||
|
||||
export function useTrace({ origin, traceId, spanId }: TraceInput) {
|
||||
const { isUpToDate, data } = useShape({
|
||||
url: `${origin}/sync/traces/${traceId}`,
|
||||
});
|
||||
|
||||
const events = prepareTrace(data as QueriedEvent[]);
|
||||
if (!events) {
|
||||
return { isUpToDate, trace: undefined };
|
||||
}
|
||||
|
||||
const trace = createTraceTreeFromEvents(events, spanId);
|
||||
return { isUpToDate, trace };
|
||||
}
|
||||
+20
-8
@@ -1,3 +1,4 @@
|
||||
import { useShape } from "@electric-sql/react";
|
||||
import {
|
||||
ArrowUturnLeftIcon,
|
||||
BoltSlashIcon,
|
||||
@@ -80,8 +81,9 @@ import {
|
||||
} from "~/utils/pathBuilder";
|
||||
import { SpanView } from "../resources.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.spans.$spanParam/route";
|
||||
import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route";
|
||||
|
||||
type TraceEvent = NonNullable<SerializeFrom<typeof loader>["trace"]>["events"][0];
|
||||
import { Trace, TraceEvent, useTrace } from "~/hooks/useTrace";
|
||||
import { trace } from "console";
|
||||
import { useAppOrigin } from "~/hooks/useAppOrigin";
|
||||
|
||||
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
const userId = await requireUserId(request);
|
||||
@@ -100,9 +102,8 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
||||
|
||||
return json({
|
||||
run: result.run,
|
||||
trace: result.trace,
|
||||
maximumLiveReloadingSetting: env.MAXIMUM_LIVE_RELOADING_EVENTS,
|
||||
resizeSettings,
|
||||
maximumLiveReloadingSetting: env.MAXIMUM_LIVE_RELOADING_EVENTS,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -114,8 +115,15 @@ function getSpanId(location: Location<any>): string | undefined {
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const { run, trace, resizeSettings, maximumLiveReloadingSetting } =
|
||||
useLoaderData<typeof loader>();
|
||||
const { run, resizeSettings, maximumLiveReloadingSetting } = useLoaderData<typeof loader>();
|
||||
const appOrigin = useAppOrigin();
|
||||
const { location, replaceSearchParam } = useReplaceLocation();
|
||||
|
||||
const { isUpToDate, trace } = useTrace({
|
||||
origin: appOrigin,
|
||||
traceId: run.traceId,
|
||||
spanId: getSpanId(location) ?? run.spanId,
|
||||
});
|
||||
const user = useUser();
|
||||
const organization = useOrganization();
|
||||
const project = useProject();
|
||||
@@ -223,7 +231,11 @@ export default function Page() {
|
||||
);
|
||||
}
|
||||
|
||||
function TraceView({ run, trace, maximumLiveReloadingSetting, resizeSettings }: LoaderData) {
|
||||
type TraceData = Pick<LoaderData, "run" | "maximumLiveReloadingSetting" | "resizeSettings"> & {
|
||||
trace: Trace | undefined;
|
||||
};
|
||||
|
||||
function TraceView({ run, trace, maximumLiveReloadingSetting, resizeSettings }: TraceData) {
|
||||
const organization = useOrganization();
|
||||
const project = useProject();
|
||||
const { location, replaceSearchParam } = useReplaceLocation();
|
||||
@@ -301,7 +313,7 @@ function TraceView({ run, trace, maximumLiveReloadingSetting, resizeSettings }:
|
||||
);
|
||||
}
|
||||
|
||||
function NoLogsView({ run, resizeSettings }: LoaderData) {
|
||||
function NoLogsView({ run, resizeSettings }: TraceData) {
|
||||
const plan = useCurrentPlan();
|
||||
const organization = useOrganization();
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { LoaderFunctionArgs } from "@remix-run/node";
|
||||
import { env } from "~/env.server";
|
||||
|
||||
export async function loader({ params, request }: LoaderFunctionArgs) {
|
||||
const url = new URL(request.url);
|
||||
const { table } = params;
|
||||
const originUrl = new URL(`${env.ELECTRIC_ORIGIN}/v1/shape/public."TaskEvent"`);
|
||||
url.searchParams.forEach((value, key) => {
|
||||
originUrl.searchParams.set(key, value);
|
||||
});
|
||||
|
||||
originUrl.searchParams.set("where", `"traceId"='${params.traceId}'`);
|
||||
|
||||
// When proxying long-polling requests, content-encoding & content-length are added
|
||||
// erroneously (saying the body is gzipped when it's not) so we'll just remove
|
||||
// them to avoid content decoding errors in the browser.
|
||||
//
|
||||
// Similar-ish problem to https://github.com/wintercg/fetch/issues/23
|
||||
let response = await fetch(originUrl.toString());
|
||||
if (response.headers.get(`content-encoding`)) {
|
||||
const headers = new Headers(response.headers);
|
||||
headers.delete(`content-encoding`);
|
||||
headers.delete(`content-length`);
|
||||
response = new Response(response.body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers,
|
||||
});
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -46,6 +46,7 @@
|
||||
"@conform-to/react": "^0.6.1",
|
||||
"@conform-to/zod": "^0.6.1",
|
||||
"@depot/sdk-node": "^0.5.0",
|
||||
"@electric-sql/react": "^0.3.1",
|
||||
"@headlessui/react": "^1.7.8",
|
||||
"@heroicons/react": "^2.0.12",
|
||||
"@internationalized/date": "^3.5.1",
|
||||
|
||||
Generated
+30
@@ -231,6 +231,9 @@ importers:
|
||||
'@depot/sdk-node':
|
||||
specifier: ^0.5.0
|
||||
version: 0.5.0
|
||||
'@electric-sql/react':
|
||||
specifier: ^0.3.1
|
||||
version: 0.3.1(react@18.2.0)
|
||||
'@headlessui/react':
|
||||
specifier: ^1.7.8
|
||||
version: 1.7.8(react-dom@18.2.0)(react@18.2.0)
|
||||
@@ -4124,6 +4127,25 @@ packages:
|
||||
'@connectrpc/connect-node': 0.13.2(@bufbuild/protobuf@1.7.2)
|
||||
dev: false
|
||||
|
||||
/@electric-sql/client@0.3.2:
|
||||
resolution: {integrity: sha512-DVOtrIfg7xlXpU95F53MnLXLeIn1eYkSRz01vj63NzZZ7bf6vLSHzNKvil0413C3U3769btfQytGNWmsb744WQ==}
|
||||
optionalDependencies:
|
||||
'@rollup/rollup-darwin-arm64': 4.21.1
|
||||
dev: false
|
||||
|
||||
/@electric-sql/react@0.3.1(react@18.2.0):
|
||||
resolution: {integrity: sha512-U+7ssfS+4UuGYXTyqGqXJGN04hrwjI8qKcv5l1br59EX93yfrFVVEfRsCQIe0mTiFTS/im9k+SUBIPrC/g8n2A==}
|
||||
peerDependencies:
|
||||
react: ^18.3.1
|
||||
peerDependenciesMeta:
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@electric-sql/client': 0.3.2
|
||||
react: 18.2.0
|
||||
use-sync-external-store: 1.2.2(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@emotion/hash@0.9.0:
|
||||
resolution: {integrity: sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==}
|
||||
dev: true
|
||||
@@ -11590,6 +11612,14 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-darwin-arm64@4.21.1:
|
||||
resolution: {integrity: sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-darwin-x64@4.13.2:
|
||||
resolution: {integrity: sha512-yUoEvnH0FBef/NbB1u6d3HNGyruAKnN74LrPAfDQL3O32e3k3OSfLrPgSJmgb3PJrBZWfPyt6m4ZhAFa2nZp2A==}
|
||||
cpu: [x64]
|
||||
|
||||
Reference in New Issue
Block a user