mirror of
https://github.com/element-hq/matrix-authentication-service.git
synced 2026-10-16 00:11:57 +00:00
Instead of injecting configuration through inline <script> tags setting window globals, server-rendered templates now carry the configuration as data attributes on the entrypoint mount node, validated client-side with a valibot schema over element.dataset. This removes all inline scripts from the templates, which is friendlier to CSP, and gives the frontend runtime-validated, typed configuration. The DOM read and the wiring now happen in the entrypoints themselves: main.tsx parses the mount node dataset, owns the QueryClient, and injects the basepath and GraphQL endpoint into the router and request layer instead of those being module-scope singletons.
75 lines
2.0 KiB
TypeScript
75 lines
2.0 KiB
TypeScript
// Copyright 2025, 2026 Element Creations Ltd.
|
|
// Copyright 2024, 2025 New Vector Ltd.
|
|
// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
// Please see LICENSE files in the repository root for full details.
|
|
|
|
import type { ExecutionResult } from "graphql";
|
|
import type { TypedDocumentString } from "./gql/graphql";
|
|
|
|
let graphqlEndpoint = "/graphql";
|
|
|
|
/**
|
|
* Sets the GraphQL endpoint to use for requests.
|
|
* This is called during initialization, once config has been loaded.
|
|
*/
|
|
export const setGraphqlEndpoint = (endpoint: string): void => {
|
|
graphqlEndpoint = endpoint;
|
|
};
|
|
|
|
type RequestOptions<TData, TVariables> = {
|
|
query: TypedDocumentString<TData, TVariables>;
|
|
signal?: AbortSignal;
|
|
// biome-ignore lint/suspicious/noExplicitAny: this is for inference
|
|
} & (TVariables extends Record<any, never>
|
|
? { variables?: TVariables }
|
|
: { variables: TVariables });
|
|
|
|
export const graphqlRequest = async <TData, TVariables>({
|
|
query,
|
|
variables,
|
|
signal,
|
|
}: RequestOptions<TData, TVariables>): Promise<TData> => {
|
|
const endpoint =
|
|
import.meta.env.TEST && typeof window === "undefined"
|
|
? new URL(graphqlEndpoint, "http://localhost/").toString()
|
|
: new URL(graphqlEndpoint, window.location.toString()).toString();
|
|
|
|
let response: Response;
|
|
try {
|
|
response = await fetch(endpoint, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
query,
|
|
variables,
|
|
}),
|
|
signal,
|
|
});
|
|
} catch (cause) {
|
|
throw new Error(`GraphQL request to ${endpoint} request failed`, {
|
|
cause,
|
|
});
|
|
}
|
|
|
|
if (!response.ok) {
|
|
throw new Error(
|
|
`GraphQL request to ${endpoint} failed: ${response.status}`,
|
|
);
|
|
}
|
|
|
|
const json: ExecutionResult<TData> = await response.json();
|
|
if (json.errors) {
|
|
throw new Error(JSON.stringify(json.errors));
|
|
}
|
|
|
|
if (!json.data) {
|
|
throw new Error(`GraphQL request to ${endpoint} returned no data`);
|
|
}
|
|
|
|
return json.data;
|
|
};
|