This commit is contained in:
Stefan Pejcic
2024-11-07 19:03:37 +01:00
parent c6df945ed5
commit 09f9f9502d
2472 changed files with 620417 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
import React from "react";
import { DevtoolsEvent } from "./event-types";
import { send } from "./send";
import { receive } from "./receive";
type DevtoolsContextValue = {
__devtools: boolean;
httpUrl: string;
wsUrl: string;
ws: WebSocket | null;
};
export const DevToolsContext = React.createContext<DevtoolsContextValue>({
__devtools: false,
httpUrl: "http://localhost:5001",
wsUrl: "ws://localhost:5001",
ws: null,
});
type Props = React.PropsWithChildren<{
__devtools?: boolean;
url?: string | [httpUrl: string, wsUrl: string];
}>;
export const DevToolsContextProvider: React.FC<Props> = ({
__devtools,
url = ["http://localhost:5001", "ws://localhost:5001"],
children,
}) => {
const httpUrl = Array.isArray(url) ? url[0] : url;
const wsUrl = Array.isArray(url)
? url[1]
: url.replace(/http(s)?:\/\//, "ws$1://");
const [values, setValues] = React.useState<DevtoolsContextValue>({
__devtools: __devtools ?? false,
httpUrl,
wsUrl,
ws: null,
});
const [ws, setWs] = React.useState<WebSocket | null>(null);
React.useEffect(() => {
let timeout: NodeJS.Timeout | null = null;
const wsInstance = new WebSocket(values.wsUrl);
wsInstance.addEventListener("open", () => {
if (!values.__devtools) {
timeout = setTimeout(() => {
send(wsInstance, DevtoolsEvent.DEVTOOLS_INIT, {
url: window.location.origin,
});
}, 300);
}
});
setWs(wsInstance);
return () => {
if (timeout) clearTimeout(timeout);
// In strict mode, the WebSocket instance might not be connected yet
// so we need to wait for it to connect before closing it
// otherwise it will log an unnecessary error in the console
if (wsInstance.readyState === WebSocket.CONNECTING) {
wsInstance.addEventListener("open", () => {
wsInstance.close(1000, window.location.origin);
});
} else {
wsInstance.close(1000, window.location.origin);
}
};
}, []);
const contextValues = React.useMemo<DevtoolsContextValue>(
() => ({
...values,
ws,
}),
[values, ws],
);
return (
<DevToolsContext.Provider value={contextValues}>
{children}
</DevToolsContext.Provider>
);
};

View File

@@ -0,0 +1,85 @@
import type {
Mutation,
MutationKey,
MutationStatus,
QueryKey,
QueryState,
QueryStatus,
} from "@tanstack/react-query";
import type { TraceType } from "./trace";
export enum DevtoolsEvent {
RELOAD = "devtools:reload",
DEVTOOLS_INIT = "devtools:init",
DEVTOOLS_ALREADY_CONNECTED = "devtools:already-connected",
ACTIVITY = "devtools:send-activity",
DEVTOOLS_ACTIVITY_UPDATE = "devtools:activity-update",
DEVTOOLS_CONNECTED_APP = "devtools:connected-app",
DEVTOOLS_DISCONNECTED_APP = "devtools:disconnected-app",
DEVTOOLS_HIGHLIGHT_IN_MONITOR = "devtools:highlight-in-monitor",
DEVTOOLS_HIGHLIGHT_IN_MONITOR_ACTION = "devtools:highlight-in-monitor-action",
DEVTOOLS_LOGIN_SUCCESS = "devtools:login-success",
DEVTOOLS_DISPLAY_LOGIN_FAILURE = "devtools:display-login-failure",
DEVTOOLS_LOGIN_FAILURE = "devtools:login-failure",
DEVTOOLS_RELOAD_AFTER_LOGIN = "devtools:reload-after-login",
DEVTOOLS_INVALIDATE_QUERY = "devtools:invalidate-query",
DEVTOOLS_INVALIDATE_QUERY_ACTION = "devtools:invalidate-query-action",
}
type Timestamps = {
createdAt: number;
updatedAt: number;
};
type ActivityPayload =
| {
type: "mutation";
identifier: string;
key?: MutationKey;
status?: MutationStatus;
trace?: TraceType[];
state: Mutation<any, any, any, any>["state"];
variables?: Mutation<any, any, any, any>["state"]["variables"];
hookName: string;
resourcePath: string | null;
resourceName?: string;
legacyKey: boolean;
}
| {
type: "query";
identifier: string;
key?: QueryKey;
status?: QueryStatus;
trace?: TraceType[];
state: QueryState<any, any>;
hookName: string;
resourcePath: string | null;
resourceName?: string;
legacyKey: boolean;
};
export type DevtoolsEventPayloads = {
[DevtoolsEvent.RELOAD]: {};
[DevtoolsEvent.DEVTOOLS_INIT]: { url: string };
[DevtoolsEvent.DEVTOOLS_ALREADY_CONNECTED]: { url: string };
[DevtoolsEvent.ACTIVITY]: ActivityPayload;
[DevtoolsEvent.DEVTOOLS_ACTIVITY_UPDATE]: {
updatedActivities: (ActivityPayload & Timestamps)[];
};
[DevtoolsEvent.DEVTOOLS_CONNECTED_APP]: { url: string | null };
[DevtoolsEvent.DEVTOOLS_DISCONNECTED_APP]: {};
[DevtoolsEvent.DEVTOOLS_HIGHLIGHT_IN_MONITOR]: { name: string };
[DevtoolsEvent.DEVTOOLS_HIGHLIGHT_IN_MONITOR_ACTION]: { name: string };
[DevtoolsEvent.DEVTOOLS_LOGIN_SUCCESS]: {};
[DevtoolsEvent.DEVTOOLS_LOGIN_FAILURE]: {
error: string | null;
code: string | null;
};
[DevtoolsEvent.DEVTOOLS_DISPLAY_LOGIN_FAILURE]: {
error: string | null;
code: string | null;
};
[DevtoolsEvent.DEVTOOLS_RELOAD_AFTER_LOGIN]: {};
[DevtoolsEvent.DEVTOOLS_INVALIDATE_QUERY]: { queryKey: QueryKey };
[DevtoolsEvent.DEVTOOLS_INVALIDATE_QUERY_ACTION]: { queryKey: QueryKey };
};

View File

@@ -0,0 +1,12 @@
export type FeedSection = {
content: string;
// frontmatter
title: string;
featured?: boolean;
date: string;
author: string;
avatar: string;
cover?: string;
};
export type Feed = FeedSection[];

View File

@@ -0,0 +1,14 @@
export { DevtoolsEvent, DevtoolsEventPayloads } from "./event-types.js";
export { TraceType } from "./trace.js";
export { Feed, FeedSection } from "./feed.js";
export {
PackageType,
PackageLatestVersionType,
AvailablePackageType,
} from "./package.js";
export { RefineHook, Scopes, hooksByScope, scopes } from "./scopes.js";
export { DevToolsContextProvider, DevToolsContext } from "./context.js";
export { send } from "./send.js";
export { receive } from "./receive.js";

View File

@@ -0,0 +1,19 @@
export type PackageType = {
name: string;
currentVersion: string;
description?: string;
changelog?: string;
documentation?: string;
};
export type PackageLatestVersionType = {
name: string;
latestVersion: string;
};
export type AvailablePackageType = {
name: string;
description: string;
install: string;
usage: string;
};

View File

@@ -0,0 +1,22 @@
// receive ws message by adding a listener to the ws object
import type { DevtoolsEvent, DevtoolsEventPayloads } from "./event-types";
export function receive<T extends DevtoolsEvent>(
ws: WebSocket,
event: T,
callback: (payload: DevtoolsEventPayloads[T]) => void,
) {
const listener = (e: MessageEvent) => {
const { event: receivedEvent, payload } = JSON.parse(e.data);
if (event === receivedEvent) {
callback(payload);
}
};
ws.addEventListener("message", listener);
return () => {
ws.removeEventListener("message", listener);
};
}

View File

@@ -0,0 +1,67 @@
export type RefineHook =
| "useCan"
| "useLog"
| "useLogList"
| "useCreate"
| "useCreateMany"
| "useCustom"
| "useCustomMutation"
| "useDelete"
| "useDeleteMany"
| "useInfiniteList"
| "useList"
| "useMany"
| "useOne"
| "useUpdate"
| "useUpdateMany"
| "useForgotPassword"
| "useGetIdentity"
| "useIsAuthenticated"
| "useLogin"
| "useLogout"
| "useOnError"
| "usePermissions"
| "useRegister"
| "useUpdatePassword";
export type Scopes = "data" | "audit-log" | "access-control" | "auth";
export const scopes: Record<RefineHook, Scopes> = {
useCan: "access-control",
useLog: "audit-log",
useLogList: "audit-log",
useCreate: "data",
useCreateMany: "data",
useCustom: "data",
useCustomMutation: "data",
useDelete: "data",
useDeleteMany: "data",
useInfiniteList: "data",
useList: "data",
useMany: "data",
useOne: "data",
useUpdate: "data",
useUpdateMany: "data",
useForgotPassword: "auth",
useGetIdentity: "auth",
useIsAuthenticated: "auth",
useLogin: "auth",
useLogout: "auth",
useOnError: "auth",
usePermissions: "auth",
useRegister: "auth",
useUpdatePassword: "auth",
};
export const hooksByScope = Object.entries(scopes).reduce(
(acc, [hook, scope]) => {
if (!acc[scope]) {
acc[scope] = [];
}
acc[scope].push(hook as RefineHook);
return acc;
},
{} as Record<Scopes, RefineHook[]>,
);

View File

@@ -0,0 +1,24 @@
import type { DevtoolsEvent, DevtoolsEventPayloads } from "./event-types";
export async function send<T extends DevtoolsEvent>(
ws: WebSocket,
event: T,
payload: DevtoolsEventPayloads[T],
) {
// check if the socket is open
// if not, wait for it to open
if (ws.readyState !== ws.OPEN) {
await new Promise<void>((resolve) => {
const listener = () => {
ws.send(JSON.stringify({ event, payload }));
resolve();
ws.removeEventListener("open", listener);
};
ws.addEventListener("open", listener);
});
return;
}
ws.send(JSON.stringify({ event, payload }));
return;
}

View File

@@ -0,0 +1,8 @@
export type TraceType = {
file?: string;
line?: number;
column?: number;
function?: string;
isRefine: boolean;
packageName?: string;
};