@manta-eu/horns-client 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/component-api.d.ts +7 -0
- package/dist/app/coverage-run.js +4 -3
- package/dist/app/home-page.js +6 -4
- package/dist/app/horn-page.js +7 -10
- package/dist/app/horn-studio.js +63 -10
- package/dist/app/horn-view-page.js +13 -9
- package/dist/app/horns-page.js +6 -9
- package/dist/app/new-horn-page.js +52 -10
- package/dist/app/paths.d.ts +15 -10
- package/dist/app/paths.js +18 -29
- package/dist/app/plans-page.d.ts +1 -0
- package/dist/app/plans-page.js +59 -0
- package/dist/app/reference-page.d.ts +1 -0
- package/dist/app/reference-page.js +55 -0
- package/dist/app/routes.js +7 -5
- package/dist/app/rule-page.js +18 -11
- package/dist/app/rules-page.js +12 -12
- package/dist/app/shape-entity-page.js +7 -9
- package/dist/app/shape-zoo-page.js +9 -10
- package/dist/app/shapes-page.js +13 -15
- package/dist/app/showcase-page.js +8 -11
- package/dist/components/app-shell.js +8 -7
- package/dist/components/catalog-card-view.js +4 -5
- package/dist/components/explain-dialog.d.ts +5 -0
- package/dist/components/explain-dialog.js +26 -0
- package/dist/components/plan-runs.js +2 -2
- package/dist/components/read-the-code.d.ts +19 -0
- package/dist/components/read-the-code.js +17 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js +2 -3
- package/dist/lib/horn-review.d.ts +7 -0
- package/dist/lib/horn-review.js +15 -0
- package/dist/lib/horn-variables.d.ts +8 -0
- package/dist/lib/horn-variables.js +39 -0
- package/dist/lib/manta/sign-in.d.ts +2 -0
- package/dist/lib/manta/sign-in.js +9 -0
- package/dist/lib/manta/sparql-select.d.ts +6 -2
- package/dist/lib/manta/sparql-select.js +17 -14
- package/dist/lib/reference/fake-sparql.d.ts +4 -0
- package/dist/lib/reference/fake-sparql.js +216 -0
- package/dist/lib/shapes/ldkit-source.d.ts +1 -1
- package/dist/lib/shapes/ldkit-source.js +1 -1
- package/dist/lib/studio-progress.js +3 -1
- package/dist/lib/surface/declarations.d.ts +5 -0
- package/dist/lib/surface/declarations.js +77 -0
- package/dist/lib/written-surface.js +13 -2
- package/dist/standalone/app.d.ts +2 -0
- package/dist/standalone/app.js +57 -0
- package/dist/standalone/client.d.ts +15 -0
- package/dist/{client.js → standalone/client.js} +12 -8
- package/dist/standalone/gates.d.ts +10 -0
- package/dist/standalone/gates.js +15 -0
- package/dist/styles.css +1 -1
- package/dist/vite/component-api.d.ts +8 -0
- package/dist/vite/component-api.js +24 -0
- package/dist/vite/index.d.ts +3 -0
- package/dist/vite/index.js +3 -0
- package/dist/vite/shape-catalog.d.ts +10 -0
- package/dist/vite/shape-catalog.js +35 -0
- package/dist/vite/typecheck-fs.d.ts +6 -0
- package/dist/vite/typecheck-fs.js +68 -0
- package/package.json +36 -12
- package/shape-catalog.d.ts +9 -0
- package/typecheck-fs.d.ts +6 -0
- package/dist/app/session-gate.d.ts +0 -6
- package/dist/app/session-gate.js +0 -19
- package/dist/client.d.ts +0 -10
- package/dist/lib/manta/providers.d.ts +0 -10
- package/dist/lib/manta/providers.js +0 -48
- package/dist/lib/manta/session.d.ts +0 -47
- package/dist/lib/manta/session.js +0 -204
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { moduleOfExport, sdkModules, surfaceExports } from "@manta-eu/horns/render";
|
|
5
|
+
/** Above this an entry is a type tree nobody can read, and the walk stopped somewhere it should not have. */
|
|
6
|
+
const maxEntryChars = 6000;
|
|
7
|
+
/** Node only: `import.meta.resolve` is unavailable under vitest's transform, so a test passes its own root. */
|
|
8
|
+
export function sdkDistRoot() {
|
|
9
|
+
return join(dirname(dirname(fileURLToPath(import.meta.resolve("@manta-eu/react")))), "dist");
|
|
10
|
+
}
|
|
11
|
+
function declarationFilesIn(directory) {
|
|
12
|
+
return readdirSync(directory).flatMap((entry) => {
|
|
13
|
+
const path = join(directory, entry);
|
|
14
|
+
if (statSync(path).isDirectory())
|
|
15
|
+
return declarationFilesIn(path);
|
|
16
|
+
return path.endsWith(".d.ts") ? [path] : [];
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/** A declaration and its body, from `export …` to the `;` that closes it. */
|
|
20
|
+
function blockOf(texts, name) {
|
|
21
|
+
const pattern = new RegExp(`^export (?:declare (?:const|function|class)|type|interface) ${name}\\b`, "m");
|
|
22
|
+
for (const text of texts) {
|
|
23
|
+
const start = text.search(pattern);
|
|
24
|
+
if (start < 0)
|
|
25
|
+
continue;
|
|
26
|
+
let depth = 0;
|
|
27
|
+
for (let at = start; at < text.length; at += 1) {
|
|
28
|
+
const character = text[at];
|
|
29
|
+
if (character === "{")
|
|
30
|
+
depth += 1;
|
|
31
|
+
else if (character === "}")
|
|
32
|
+
depth -= 1;
|
|
33
|
+
else if (character === ";" && depth === 0)
|
|
34
|
+
return text.slice(start, at + 1);
|
|
35
|
+
}
|
|
36
|
+
return text.slice(start);
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The export's declaration plus the ones it names, transitively.
|
|
42
|
+
*
|
|
43
|
+
* A props type sits beside its component and may name a schema elsewhere — `BarChartProps` reaches
|
|
44
|
+
* `BarChartData` reaches `barChartSchema`. Following the names keeps the answer to what the export is,
|
|
45
|
+
* where a whole directory would hand over every chart in the design system.
|
|
46
|
+
*/
|
|
47
|
+
function scopeOf(texts, name, hops) {
|
|
48
|
+
const first = blockOf(texts, name);
|
|
49
|
+
if (first === undefined)
|
|
50
|
+
return undefined;
|
|
51
|
+
const seen = new Set([name]);
|
|
52
|
+
const blocks = [first];
|
|
53
|
+
for (let hop = 0; hop < hops; hop += 1) {
|
|
54
|
+
const named = [...blocks.join("\n").matchAll(/\b([A-Za-z_$][\w$]*)\b/g)].flatMap((match) => match[1] ?? []);
|
|
55
|
+
for (const referenced of named) {
|
|
56
|
+
if (seen.has(referenced))
|
|
57
|
+
continue;
|
|
58
|
+
seen.add(referenced);
|
|
59
|
+
const block = blockOf(texts, referenced);
|
|
60
|
+
if (block !== undefined)
|
|
61
|
+
blocks.push(block);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return blocks.join("\n");
|
|
65
|
+
}
|
|
66
|
+
export function componentApiFrom(root, hops = 3) {
|
|
67
|
+
const texts = declarationFilesIn(root).map((path) => readFileSync(path, "utf8"));
|
|
68
|
+
// Only the design system's own: React's hooks and the kit's plan module are not its to declare.
|
|
69
|
+
const names = [...new Set(sdkModules.flatMap((module) => [...(surfaceExports(module) ?? [])]))].sort();
|
|
70
|
+
return Object.freeze(Object.fromEntries(names.flatMap((name) => {
|
|
71
|
+
const scope = scopeOf(texts, name, hops);
|
|
72
|
+
if (scope === undefined || scope.length > maxEntryChars)
|
|
73
|
+
return [];
|
|
74
|
+
const from = moduleOfExport(name);
|
|
75
|
+
return [[name, `// ${name}, from "${from ?? "@manta-eu/react"}"\n${scope}`]];
|
|
76
|
+
})));
|
|
77
|
+
}
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { useCallback, useId, useMemo, useState } from "react";
|
|
2
|
+
import { viewPowersModule } from "@manta-eu/horns/render";
|
|
2
3
|
// The whole barrel, because a view may reach for any of it; `createSurfaceLoader` narrows it to the declared
|
|
3
4
|
// exports before the view sees it. Dynamic, and held at module scope: `WrittenView` recompiles whenever this
|
|
4
5
|
// identity changes.
|
|
5
6
|
export const loadWrittenSurface = async () => {
|
|
6
|
-
const [react, chart] = await Promise.all([
|
|
7
|
-
|
|
7
|
+
const [react, chart, plan, explain] = await Promise.all([
|
|
8
|
+
import("@manta-eu/react"),
|
|
9
|
+
import("@manta-eu/react/chart"),
|
|
10
|
+
import("@manta-eu/horns-kit/plan"),
|
|
11
|
+
import("@manta-eu/horns-kit/view"),
|
|
12
|
+
]);
|
|
13
|
+
return {
|
|
14
|
+
"@manta-eu/react": react,
|
|
15
|
+
"@manta-eu/react/chart": chart,
|
|
16
|
+
"@manta-eu/horns-kit/plan": plan,
|
|
17
|
+
[viewPowersModule]: explain,
|
|
18
|
+
};
|
|
8
19
|
};
|
|
9
20
|
// This app's React, so a view's state lives in the same reconciler as the page rather than in a second copy.
|
|
10
21
|
export const writtenHooks = { useState, useMemo, useCallback, useId };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth } from "@manta-eu/react/hooks/useAuth";
|
|
3
|
+
import { AuthContext } from "@manta-eu/react/providers/AuthContext";
|
|
4
|
+
import { useActiveDatastore } from "@manta-eu/react/providers";
|
|
5
|
+
import { isPresent } from "@manta-eu/lib/present";
|
|
6
|
+
import { signedInParam } from "@manta-eu/horns-server/shared";
|
|
7
|
+
import { AppRoutes } from "../app/routes";
|
|
8
|
+
import { Centered, SignIn, SignInLooped } from "./gates";
|
|
9
|
+
import { DatastoreMissing } from "../components/states";
|
|
10
|
+
import { useCustomerDatastoresQuery } from "@manta-eu/client";
|
|
11
|
+
import { useMantaClient } from "@manta-eu/react/providers";
|
|
12
|
+
// Horns' `/login` stamps this, so a return carrying it with no session is a round trip that did not
|
|
13
|
+
// complete rather than a visitor who has not tried. `useAuth` has no equivalent and its generic path loops.
|
|
14
|
+
const justSignedIn = () => new URLSearchParams(window.location.search).get(signedInParam) === "1";
|
|
15
|
+
// Keycloak hands a failure back on the URL. `useAuth` cannot see it, and without this the page offers
|
|
16
|
+
// "sign in" again to someone whose sign-in just failed.
|
|
17
|
+
const authError = () => new URLSearchParams(window.location.search).get("auth_error") ?? undefined;
|
|
18
|
+
/** The session gate, for a Horns that is the whole page. A host that has already gated mounts none of it. */
|
|
19
|
+
export function HornsApp() {
|
|
20
|
+
const auth = useAuth();
|
|
21
|
+
const { datastoreId } = useActiveDatastore();
|
|
22
|
+
const { mantaClient } = useMantaClient();
|
|
23
|
+
// `sessionContext` lists the datastores a member belongs to, which for an operator is none.
|
|
24
|
+
const customers = useCustomerDatastoresQuery(mantaClient, undefined, { enabled: auth.isOperator });
|
|
25
|
+
const datastores = auth.isOperator ? (customers.data?.customerDatastores ?? []) : auth.datastores;
|
|
26
|
+
const failed = authError();
|
|
27
|
+
if (isPresent(failed))
|
|
28
|
+
return _jsx(Centered, { role: "alert", children: failed });
|
|
29
|
+
if (auth.isLoading)
|
|
30
|
+
return _jsx(Centered, { children: "Loading\u2026" });
|
|
31
|
+
if (isPresent(auth.error))
|
|
32
|
+
return _jsx(Centered, { role: "alert", children: auth.error });
|
|
33
|
+
if (!auth.isSessionValid)
|
|
34
|
+
return justSignedIn() ? _jsx(SignInLooped, {}) : _jsx(SignIn, {});
|
|
35
|
+
if (auth.contextFailed)
|
|
36
|
+
return _jsx(Centered, { role: "alert", children: "Could not load your account. Try again." });
|
|
37
|
+
if (!isPresent(auth.user))
|
|
38
|
+
return _jsx(Centered, { children: "Loading\u2026" });
|
|
39
|
+
// Refused here rather than left to a page to render empty, in the words every page already used.
|
|
40
|
+
if (isPresent(datastoreId) && !auth.datastoreAccessGranted) {
|
|
41
|
+
return (_jsx("main", { className: "grid min-h-screen place-items-center p-8", children: _jsx(DatastoreMissing, {}) }));
|
|
42
|
+
}
|
|
43
|
+
return (_jsx(AuthContext.Provider, { value: {
|
|
44
|
+
user: auth.user,
|
|
45
|
+
datastore: auth.datastore,
|
|
46
|
+
datastores,
|
|
47
|
+
logout: auth.logout,
|
|
48
|
+
isOperator: auth.isOperator,
|
|
49
|
+
canManageTeam: auth.canManageTeam,
|
|
50
|
+
canManageIntegrations: auth.canManageIntegrations,
|
|
51
|
+
canManageStreams: auth.canManageStreams,
|
|
52
|
+
canManageSharedMemories: auth.canManageSharedMemories,
|
|
53
|
+
canSwitchDatastore: auth.canSwitchDatastore,
|
|
54
|
+
allowedModels: auth.allowedModels,
|
|
55
|
+
orgRoles: auth.orgRoles,
|
|
56
|
+
}, children: _jsx(AppRoutes, {}) }));
|
|
57
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ShapeCatalogSource } from "../lib/shapes/catalog";
|
|
2
|
+
import "../app/globals.css";
|
|
3
|
+
export type MountHornsOptions = {
|
|
4
|
+
/** The realm's shapes. Horns ships none: they describe a deployment's data model, not the library's. */
|
|
5
|
+
shapes: ShapeCatalogSource;
|
|
6
|
+
/** Defaults to `#app`, which is what this package's own document declares. */
|
|
7
|
+
container?: HTMLElement;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Mount Horns as a whole page. A host with a router of its own splices `hornsRoutes()` in instead.
|
|
11
|
+
*
|
|
12
|
+
* The base URL is this deployment's origin, never agents': the browser holds this worker's session cookies
|
|
13
|
+
* and none of agents', so every SDK call has to land here to be proxied with them.
|
|
14
|
+
*/
|
|
15
|
+
export declare function mountHorns({ shapes, container }: MountHornsOptions): void;
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { StrictMode } from "react";
|
|
3
3
|
import { createRoot } from "react-dom/client";
|
|
4
|
-
import { BrowserRouter } from "react-router";
|
|
5
4
|
import { ErrorBoundary } from "@sentry/react";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import "
|
|
5
|
+
import { Providers } from "@manta-eu/react/providers";
|
|
6
|
+
import { provideShapeCatalog } from "../lib/shapes/catalog";
|
|
7
|
+
import { initSentry } from "../lib/telemetry/sentry";
|
|
8
|
+
import { HornsApp } from "./app";
|
|
9
|
+
import "../app/globals.css";
|
|
11
10
|
function AppCrashed() {
|
|
12
11
|
return (_jsx("main", { className: "grid min-h-screen place-items-center p-8 text-center", children: _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-semibold", children: "Something went wrong" }), _jsx("p", { className: "mt-2 text-sm opacity-70", children: "Reload the page to try again." })] }) }));
|
|
13
12
|
}
|
|
14
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Mount Horns as a whole page. A host with a router of its own splices `hornsRoutes()` in instead.
|
|
15
|
+
*
|
|
16
|
+
* The base URL is this deployment's origin, never agents': the browser holds this worker's session cookies
|
|
17
|
+
* and none of agents', so every SDK call has to land here to be proxied with them.
|
|
18
|
+
*/
|
|
15
19
|
export function mountHorns({ shapes, container }) {
|
|
16
20
|
provideShapeCatalog(shapes);
|
|
17
21
|
initSentry();
|
|
18
22
|
const root = container ?? document.getElementById("app");
|
|
19
23
|
if (!root)
|
|
20
24
|
throw new Error("mountHorns found no container: pass one, or give the document an #app element.");
|
|
21
|
-
createRoot(root).render(_jsx(StrictMode, { children: _jsx(ErrorBoundary, { fallback: _jsx(AppCrashed, {}), children: _jsx(
|
|
25
|
+
createRoot(root).render(_jsx(StrictMode, { children: _jsx(ErrorBoundary, { fallback: _jsx(AppCrashed, {}), children: _jsx(Providers, { mantaBaseUrl: window.location.origin, children: _jsx(HornsApp, {}) }) }) }));
|
|
22
26
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
type CenteredProps = {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
role?: "alert";
|
|
5
|
+
};
|
|
6
|
+
export declare function Centered({ children, role }: CenteredProps): import("react").JSX.Element;
|
|
7
|
+
export declare function SignIn(): import("react").JSX.Element;
|
|
8
|
+
/** A return from Keycloak carrying the marker but no session: the round trip did not complete. */
|
|
9
|
+
export declare function SignInLooped(): import("react").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { signInHref } from "../lib/manta/sign-in";
|
|
3
|
+
export function Centered({ children, role }) {
|
|
4
|
+
return (_jsx("main", { className: "grid min-h-screen place-items-center p-8 text-center", children: _jsx("p", { className: "text-sm opacity-70", ...(role ? { role } : {}), children: children }) }));
|
|
5
|
+
}
|
|
6
|
+
function signIn() {
|
|
7
|
+
return signInHref(`${window.location.pathname}${window.location.search}`);
|
|
8
|
+
}
|
|
9
|
+
export function SignIn() {
|
|
10
|
+
return (_jsx("main", { className: "grid min-h-screen place-items-center p-8 text-center", children: _jsxs("div", { className: "flex flex-col items-center gap-4", children: [_jsx("h1", { className: "text-xl font-semibold", children: "Sign in to Manta Horns" }), _jsx("a", { className: "rounded-md border px-4 py-2", href: signIn(), children: "Sign in" })] }) }));
|
|
11
|
+
}
|
|
12
|
+
/** A return from Keycloak carrying the marker but no session: the round trip did not complete. */
|
|
13
|
+
export function SignInLooped() {
|
|
14
|
+
return (_jsx("main", { className: "grid min-h-screen place-items-center p-8 text-center", children: _jsxs("div", { className: "flex flex-col items-center gap-4", children: [_jsx("p", { role: "alert", children: "Signing in did not complete. Try again, or ask an admin to check your access." }), _jsx("a", { className: "rounded-md border px-4 py-2", href: signIn(), children: "Sign in" })] }) }));
|
|
15
|
+
}
|