@yourrealm/sdk 0.0.0-alpha.1378.ce03c8dc
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/LICENSE-EXCEPTION +26 -0
- package/README.md +34 -0
- package/dist/chunk-3R6KEFOI.js +22 -0
- package/dist/chunk-67BDAMYX.js +376 -0
- package/dist/chunk-AITGS27R.js +139 -0
- package/dist/chunk-CMAEHQ4F.js +6 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-O3UIK34B.js +1430 -0
- package/dist/chunk-VHTUSZUX.js +176 -0
- package/dist/cli.js +9006 -0
- package/dist/docker.js +54 -0
- package/dist/index.js +1053 -0
- package/dist/ingress.js +436 -0
- package/dist/jsx-runtime.js +2 -0
- package/dist/preact.js +22 -0
- package/dist/service-url.js +11 -0
- package/dist/services.js +213 -0
- package/dist/test.js +5069 -0
- package/dist/ui.js +254 -0
- package/dist/worker-protocol.js +89 -0
- package/package.json +112 -0
- package/src/app-icon.ts +74 -0
- package/src/apply.ts +52 -0
- package/src/bundle-ui.ts +211 -0
- package/src/bundle.ts +546 -0
- package/src/cli/build-dev-landing.ts +84 -0
- package/src/cli/build-install-panel.ts +90 -0
- package/src/cli/build-runtime.ts +133 -0
- package/src/cli/build.ts +63 -0
- package/src/cli/dev.ts +107 -0
- package/src/cli/down.ts +60 -0
- package/src/cli/main.ts +56 -0
- package/src/cli/test.ts +61 -0
- package/src/cli/validate.ts +37 -0
- package/src/compile.ts +100 -0
- package/src/create-app.ts +982 -0
- package/src/deep-merge.ts +33 -0
- package/src/dev/fixture.ts +48 -0
- package/src/dev/install-handler.ts +383 -0
- package/src/dev/server.ts +968 -0
- package/src/dev/stack.ts +108 -0
- package/src/dev/state.ts +159 -0
- package/src/docker.ts +96 -0
- package/src/errors.ts +61 -0
- package/src/factory.ts +481 -0
- package/src/garage.ts +56 -0
- package/src/generator/hash.ts +6 -0
- package/src/generator/resolve-refs.ts +207 -0
- package/src/grants-builder.ts +100 -0
- package/src/grants.ts +260 -0
- package/src/home-api.ts +172 -0
- package/src/home-cookie.ts +10 -0
- package/src/home-registry.ts +120 -0
- package/src/home.ts +187 -0
- package/src/host-registry.ts +143 -0
- package/src/http-header.ts +20 -0
- package/src/identifiers.ts +49 -0
- package/src/index.ts +66 -0
- package/src/info.ts +64 -0
- package/src/ini.ts +50 -0
- package/src/json.ts +14 -0
- package/src/jsx-runtime/index.ts +5 -0
- package/src/keys.ts +29 -0
- package/src/mysql.ts +56 -0
- package/src/pack-build.ts +108 -0
- package/src/pack.ts +115 -0
- package/src/peer-registry.ts +121 -0
- package/src/peer-world.ts +117 -0
- package/src/permission-meta.ts +116 -0
- package/src/permissions.ts +108 -0
- package/src/pkg-root.ts +34 -0
- package/src/postgres.ts +79 -0
- package/src/preact/index.ts +27 -0
- package/src/redis.ts +43 -0
- package/src/ref.ts +271 -0
- package/src/remote-registry.ts +33 -0
- package/src/required-host-kinds.ts +110 -0
- package/src/resolve-context.ts +111 -0
- package/src/resolve.ts +471 -0
- package/src/runtime/docker/compose/compose-schema.ts +118 -0
- package/src/runtime/docker/compose/convert-to-docker-compose.ts +278 -0
- package/src/runtime/docker/compose/default-hardening.ts +21 -0
- package/src/runtime/docker/compose/effects.ts +107 -0
- package/src/runtime/docker/compose/index.ts +17 -0
- package/src/runtime/docker/compose/labels.ts +39 -0
- package/src/runtime/docker/compose/types.ts +78 -0
- package/src/runtime/docker/home-proxy-server/index.ts +177 -0
- package/src/runtime/docker/home-proxy-server/protocol.ts +53 -0
- package/src/runtime/docker/ingress/build-config.ts +819 -0
- package/src/runtime/docker/ingress/index.ts +16 -0
- package/src/runtime/docker/ingress/types.ts +182 -0
- package/src/runtime/docker/log-frames.ts +121 -0
- package/src/runtime/docker/logs-server/index.ts +366 -0
- package/src/runtime/docker/spawn-server/context-key.ts +82 -0
- package/src/runtime/docker/spawn-server/index.ts +492 -0
- package/src/runtime/docker/spawn-server/protocol.ts +70 -0
- package/src/runtime/docker/spawn-server/spec.ts +122 -0
- package/src/runtime/docker/stats-server/disks.ts +161 -0
- package/src/runtime/docker/stats-server/index.ts +322 -0
- package/src/runtime/home-client/index.ts +71 -0
- package/src/runtime/log-line.ts +24 -0
- package/src/runtime/logs-client/index.ts +175 -0
- package/src/runtime/spawn-client/index.ts +93 -0
- package/src/runtime/spawn-client/kaniko.ts +79 -0
- package/src/runtime/stats-client/index.ts +65 -0
- package/src/runtime/stats-sample.ts +95 -0
- package/src/safe-json.ts +37 -0
- package/src/sandbox.ts +1209 -0
- package/src/schema/build-apps.ts +38 -0
- package/src/schema/home.ts +217 -0
- package/src/schema/host.ts +792 -0
- package/src/schema/inputs.ts +26 -0
- package/src/schema/nodes.ts +75 -0
- package/src/schema/peer.ts +400 -0
- package/src/schema/public-domain.ts +116 -0
- package/src/schema/remote.ts +74 -0
- package/src/security.ts +594 -0
- package/src/serialize.ts +21 -0
- package/src/service-definition.ts +284 -0
- package/src/service-url.ts +64 -0
- package/src/service.ts +39 -0
- package/src/services/index.ts +5 -0
- package/src/shared.ts +65 -0
- package/src/speculative-silo.ts +96 -0
- package/src/stable-secret.ts +14 -0
- package/src/surfaces.ts +264 -0
- package/src/tailwind-bin.ts +56 -0
- package/src/test/boot.ts +192 -0
- package/src/test/compile.ts +501 -0
- package/src/test/describe.ts +117 -0
- package/src/test/index.ts +11 -0
- package/src/test/ingress-fixture.ts +37 -0
- package/src/test/types.ts +318 -0
- package/src/trusted-headers.ts +79 -0
- package/src/types.ts +126 -0
- package/src/ui/components/aside.tsx +96 -0
- package/src/ui/components/badge.tsx +42 -0
- package/src/ui/components/button.tsx +59 -0
- package/src/ui/components/card.tsx +106 -0
- package/src/ui/components/checkbox.tsx +37 -0
- package/src/ui/components/code-block.tsx +29 -0
- package/src/ui/components/combobox.tsx +119 -0
- package/src/ui/components/config-field.tsx +284 -0
- package/src/ui/components/dialog.tsx +137 -0
- package/src/ui/components/domain-editor.tsx +317 -0
- package/src/ui/components/domain-list.ts +205 -0
- package/src/ui/components/dropdown-menu.tsx +73 -0
- package/src/ui/components/empty-state.tsx +51 -0
- package/src/ui/components/highlight.ts +59 -0
- package/src/ui/components/icons.tsx +101 -0
- package/src/ui/components/image-posture.tsx +85 -0
- package/src/ui/components/input.tsx +28 -0
- package/src/ui/components/label.tsx +16 -0
- package/src/ui/components/line-chart.tsx +113 -0
- package/src/ui/components/page.tsx +39 -0
- package/src/ui/components/permission-row.tsx +262 -0
- package/src/ui/components/permission-section.tsx +51 -0
- package/src/ui/components/pie-chart.tsx +245 -0
- package/src/ui/components/schema-block.tsx +60 -0
- package/src/ui/components/separator.tsx +42 -0
- package/src/ui/components/sheet.tsx +130 -0
- package/src/ui/components/sidebar.tsx +650 -0
- package/src/ui/components/silo-picker.tsx +510 -0
- package/src/ui/components/skeleton.tsx +13 -0
- package/src/ui/components/sonner.tsx +33 -0
- package/src/ui/components/switch.tsx +58 -0
- package/src/ui/components/table-view.ts +219 -0
- package/src/ui/components/table.tsx +506 -0
- package/src/ui/components/tabs.tsx +59 -0
- package/src/ui/components/timeago.tsx +84 -0
- package/src/ui/components/tooltip.tsx +56 -0
- package/src/ui/components.json +21 -0
- package/src/ui/dev/dev-shell.tsx +145 -0
- package/src/ui/home/home-grid.tsx +177 -0
- package/src/ui/hooks/use-mobile.ts +19 -0
- package/src/ui/index.ts +189 -0
- package/src/ui/install/adapter.ts +193 -0
- package/src/ui/install/deploy-targets-picker.tsx +185 -0
- package/src/ui/install/install-app-panel.tsx +912 -0
- package/src/ui/install/install-error.tsx +62 -0
- package/src/ui/install/perms-inactive.ts +50 -0
- package/src/ui/install/required-fields.ts +37 -0
- package/src/ui/install/tabs-form.tsx +557 -0
- package/src/ui/tokens.css +279 -0
- package/src/ui/utils.ts +78 -0
- package/src/ui-error.ts +139 -0
- package/src/ui-runtime/dev-landing-entry.tsx +258 -0
- package/src/ui-runtime/index.ts +83 -0
- package/src/ui-runtime/install-entry.tsx +119 -0
- package/src/ui-runtime/mount.ts +367 -0
- package/src/ui-runtime/runtime.css +56 -0
- package/src/ui-surface.ts +451 -0
- package/src/valkey.ts +43 -0
- package/src/worker/extract-tar.ts +125 -0
- package/src/worker/identity.ts +183 -0
- package/src/worker/index.ts +16 -0
- package/src/worker/public-ip.ts +92 -0
- package/src/worker/queue.ts +54 -0
- package/src/worker/reconcile.ts +127 -0
- package/src/worker/start-worker.ts +227 -0
- package/src/worker/ui-proxy.ts +335 -0
- package/src/worker/uplink.ts +523 -0
- package/src/worker/validate.ts +34 -0
- package/src/worker/verify-relay.ts +102 -0
- package/src/worker-protocol/app-dirs.ts +94 -0
- package/src/worker-protocol/home-proxy-paths.ts +76 -0
- package/src/worker-protocol/index.ts +55 -0
- package/src/worker-protocol/ingress.ts +166 -0
- package/src/worker-protocol/logs-socket-path.ts +50 -0
- package/src/worker-protocol/networks.ts +39 -0
- package/src/worker-protocol/node-version.ts +70 -0
- package/src/worker-protocol/silo-paths.ts +114 -0
- package/src/worker-protocol/spawn-paths.ts +165 -0
- package/src/worker-protocol/stats-socket-path.ts +40 -0
- package/src/worker-protocol/update-files.ts +77 -0
- package/src/worker-protocol/uplink/constants.ts +86 -0
- package/src/worker-protocol/uplink/errors.ts +61 -0
- package/src/worker-protocol/uplink/frame.ts +114 -0
- package/src/worker-protocol/uplink/identity.ts +40 -0
- package/src/worker-protocol/uplink/index.ts +63 -0
- package/src/worker-protocol/uplink/methods.ts +462 -0
- package/src/worker-protocol/uplink/mux.ts +646 -0
- package/src/worker-protocol/uplink/protocol.ts +11 -0
- package/src/worker-protocol/uplink/rpc.ts +309 -0
- package/src/worker-protocol/uplink/session.ts +540 -0
- package/src/worker-protocol/uplink/test-support.ts +188 -0
- package/src/worker-protocol/worker-mount.ts +27 -0
- package/src/worker-protocol/worker-state.ts +190 -0
- package/src/xml.ts +49 -0
- package/src/yaml.ts +15 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// Browser entry for `realm-sdk dev`'s landing page. Mounts the shared
|
|
2
|
+
// `DevShell` (Home-style sidebar) around one of three views, picked from
|
|
3
|
+
// the URL pathname the server rendered:
|
|
4
|
+
//
|
|
5
|
+
// - / → Home: router tiles + widget iframes in a mixed
|
|
6
|
+
// grid (mirrors platform/home/src/routes/(authed)/index.tsx).
|
|
7
|
+
// - /pages/<key> → page iframe for that surface.
|
|
8
|
+
//
|
|
9
|
+
// The island also owns two bridges:
|
|
10
|
+
//
|
|
11
|
+
// - postMessage bridge: forward `ui-call` from sandboxed iframes to
|
|
12
|
+
// `POST /loader/<kind>/<key>/<name>`. The iframes run in an opaque
|
|
13
|
+
// origin (no allow-same-origin) so they can't fetch directly.
|
|
14
|
+
//
|
|
15
|
+
// - SSE: subscribe to `/events` and bump an iframe `key` so any visible
|
|
16
|
+
// iframe remounts on realm.ts edits.
|
|
17
|
+
//
|
|
18
|
+
// Routing is server-driven: clicking a sidebar entry triggers a full
|
|
19
|
+
// navigation; the dev server returns this same HTML for / and
|
|
20
|
+
// /pages/<key>, just with a different `pathname` baked in.
|
|
21
|
+
|
|
22
|
+
import { render } from "preact";
|
|
23
|
+
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
24
|
+
import type { WidgetManifest } from "../resolve.ts";
|
|
25
|
+
import { CubeIcon } from "../ui/components/icons.tsx";
|
|
26
|
+
import { DevShell, type DevShellState } from "../ui/dev/dev-shell.tsx";
|
|
27
|
+
import { HomeGrid, type RouterTile, type WidgetTile } from "../ui/home/home-grid.tsx";
|
|
28
|
+
|
|
29
|
+
// Wire shapes the dev server serializes into the page; the HomeGrid
|
|
30
|
+
// component takes its own (different) tile shape, so the landing maps
|
|
31
|
+
// these → tiles before rendering.
|
|
32
|
+
interface WireRouter {
|
|
33
|
+
serviceId: string;
|
|
34
|
+
name: string;
|
|
35
|
+
iconSrc?: string;
|
|
36
|
+
publicDomain?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type WireWidget = Pick<WidgetManifest, "title" | "icon" | "cols" | "rows"> & { key: string };
|
|
40
|
+
|
|
41
|
+
interface LandingState extends DevShellState {
|
|
42
|
+
pathname: string;
|
|
43
|
+
routers: WireRouter[];
|
|
44
|
+
widgets: WireWidget[];
|
|
45
|
+
sidebarOpen: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function readState(): LandingState {
|
|
49
|
+
const el = document.getElementById("dev-state");
|
|
50
|
+
if (!el || el.tagName !== "SCRIPT") throw new Error("dev-state script tag missing");
|
|
51
|
+
return JSON.parse(el.textContent ?? "{}") as LandingState;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const KEY_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
55
|
+
|
|
56
|
+
function useUiCallBridge() {
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
function findIframeWindow(source: EventTarget | null): HTMLIFrameElement | null {
|
|
59
|
+
if (!source) return null;
|
|
60
|
+
for (const f of document.querySelectorAll("iframe")) {
|
|
61
|
+
if (f.contentWindow === source) return f;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
// `/iframe/<kind>/<key>` is where this landing page mounted the surface,
|
|
66
|
+
// so the src is the authentic record of which one it is.
|
|
67
|
+
function surfaceFromIframe(
|
|
68
|
+
iframe: HTMLIFrameElement,
|
|
69
|
+
): { kind: "page" | "widget"; key: string } | null {
|
|
70
|
+
let path: string;
|
|
71
|
+
try {
|
|
72
|
+
path = new URL(iframe.src, globalThis.location.href).pathname;
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const m = path.match(/^\/iframe\/(page|widget)\/([^/]+)$/);
|
|
77
|
+
if (!m) return null;
|
|
78
|
+
let key: string;
|
|
79
|
+
try {
|
|
80
|
+
key = decodeURIComponent(m[2]!);
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
if (!KEY_RE.test(key)) return null;
|
|
85
|
+
return { kind: m[1] as "page" | "widget", key };
|
|
86
|
+
}
|
|
87
|
+
function reply(
|
|
88
|
+
target: Window,
|
|
89
|
+
id: number,
|
|
90
|
+
payload: { data: unknown } | { error: string; code?: string },
|
|
91
|
+
): void {
|
|
92
|
+
target.postMessage({ realm: "ui-response", id, ...payload }, "*");
|
|
93
|
+
}
|
|
94
|
+
async function onMessage(ev: MessageEvent): Promise<void> {
|
|
95
|
+
const v = ev.data as {
|
|
96
|
+
realm?: string;
|
|
97
|
+
id?: number;
|
|
98
|
+
endpoint?: string;
|
|
99
|
+
name?: string;
|
|
100
|
+
request?: unknown;
|
|
101
|
+
} | null;
|
|
102
|
+
if (!v || v.realm !== "ui-call" || typeof v.id !== "number") return;
|
|
103
|
+
if (typeof v.name !== "string" || !KEY_RE.test(v.name)) return;
|
|
104
|
+
if (v.endpoint !== "loader" && v.endpoint !== "action") return;
|
|
105
|
+
const source = ev.source as Window | null;
|
|
106
|
+
if (!source) return;
|
|
107
|
+
const iframe = findIframeWindow(source);
|
|
108
|
+
if (!iframe) return;
|
|
109
|
+
// The surface's identity comes off the element we mounted, never off
|
|
110
|
+
// the message — same rule Home's UiBridge enforces, so the dev loop
|
|
111
|
+
// can't accept a call shape the real parent would reject.
|
|
112
|
+
const surface = surfaceFromIframe(iframe);
|
|
113
|
+
if (!surface) return;
|
|
114
|
+
try {
|
|
115
|
+
const res = await fetch(
|
|
116
|
+
`/${v.endpoint}/${surface.kind}/${encodeURIComponent(surface.key)}/${encodeURIComponent(
|
|
117
|
+
v.name,
|
|
118
|
+
)}`,
|
|
119
|
+
{
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: { "content-type": "application/json" },
|
|
122
|
+
body: JSON.stringify({ request: v.request }),
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
const body = (await res.json().catch(() => null)) as unknown;
|
|
126
|
+
if (!res.ok) {
|
|
127
|
+
const errMsg =
|
|
128
|
+
body && typeof body === "object" && "error" in body
|
|
129
|
+
? String((body as { error: unknown }).error)
|
|
130
|
+
: `HTTP ${res.status}`;
|
|
131
|
+
const code =
|
|
132
|
+
body && typeof body === "object" && "code" in body
|
|
133
|
+
? String((body as { code: unknown }).code)
|
|
134
|
+
: undefined;
|
|
135
|
+
reply(source, v.id, { error: errMsg, code });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
reply(source, v.id, { data: body });
|
|
139
|
+
} catch (err) {
|
|
140
|
+
reply(source, v.id, { error: (err as Error)?.message ?? String(err) });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
globalThis.addEventListener("message", onMessage);
|
|
144
|
+
return () => globalThis.removeEventListener("message", onMessage);
|
|
145
|
+
}, []);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function useSseReload(onReload: () => void) {
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
const es = new EventSource("/events");
|
|
151
|
+
const handler = () => onReload();
|
|
152
|
+
es.addEventListener("reload", handler);
|
|
153
|
+
return () => {
|
|
154
|
+
es.removeEventListener("reload", handler);
|
|
155
|
+
es.close();
|
|
156
|
+
};
|
|
157
|
+
}, [onReload]);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function HomeView({
|
|
161
|
+
routers,
|
|
162
|
+
widgets,
|
|
163
|
+
bump,
|
|
164
|
+
}: {
|
|
165
|
+
routers: WireRouter[];
|
|
166
|
+
widgets: WireWidget[];
|
|
167
|
+
bump: number;
|
|
168
|
+
}) {
|
|
169
|
+
const routerTiles: RouterTile[] = routers.map((r) => ({
|
|
170
|
+
key: r.serviceId,
|
|
171
|
+
label: r.name,
|
|
172
|
+
...(r.publicDomain ? { href: `https://${r.publicDomain}`, external: true } : {}),
|
|
173
|
+
...(r.iconSrc ? { iconSrc: r.iconSrc } : {}),
|
|
174
|
+
}));
|
|
175
|
+
const widgetTiles: WidgetTile[] = widgets.map((w) => ({
|
|
176
|
+
key: w.key,
|
|
177
|
+
title: w.title,
|
|
178
|
+
icon: w.icon,
|
|
179
|
+
cols: w.cols,
|
|
180
|
+
rows: w.rows,
|
|
181
|
+
iframeSrc: `/iframe/widget/${encodeURIComponent(w.key)}`,
|
|
182
|
+
}));
|
|
183
|
+
return (
|
|
184
|
+
<HomeGrid
|
|
185
|
+
routers={routerTiles}
|
|
186
|
+
widgets={widgetTiles}
|
|
187
|
+
reloadNonce={bump}
|
|
188
|
+
emptyState={
|
|
189
|
+
<div class="rounded-2xl border border-dashed border-border bg-card/50 p-10 flex flex-col items-center gap-3 text-center">
|
|
190
|
+
<CubeIcon className="size-8 text-muted-foreground" />
|
|
191
|
+
<p class="text-sm text-muted-foreground max-w-sm">
|
|
192
|
+
This app declares no routed services or widgets. Add a{" "}
|
|
193
|
+
<code class="font-mono">router</code> to a service or a{" "}
|
|
194
|
+
<code class="font-mono">ui.widget</code> to see tiles here.
|
|
195
|
+
</p>
|
|
196
|
+
</div>
|
|
197
|
+
}
|
|
198
|
+
/>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function PageView({ pageKey, bump }: { pageKey: string; bump: number }) {
|
|
203
|
+
return (
|
|
204
|
+
<iframe
|
|
205
|
+
key={`page:${pageKey}:${bump}`}
|
|
206
|
+
sandbox="allow-scripts allow-forms"
|
|
207
|
+
src={`/iframe/page/${encodeURIComponent(pageKey)}`}
|
|
208
|
+
class="flex-1 w-full border-0 bg-card"
|
|
209
|
+
/>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function pageKeyFromPath(pathname: string): string | null {
|
|
214
|
+
const m = pathname.match(/^\/pages\/([^/]+)$/);
|
|
215
|
+
if (!m) return null;
|
|
216
|
+
try {
|
|
217
|
+
return decodeURIComponent(m[1]!);
|
|
218
|
+
} catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function DevLanding({ state }: { state: LandingState }) {
|
|
224
|
+
const [bump, setBump] = useState(0);
|
|
225
|
+
useUiCallBridge();
|
|
226
|
+
useSseReload(useMemo(() => () => setBump((n) => n + 1), []));
|
|
227
|
+
|
|
228
|
+
const pageKey = pageKeyFromPath(state.pathname);
|
|
229
|
+
const activePage = pageKey ? state.pages.find((p) => p.key === pageKey) : null;
|
|
230
|
+
|
|
231
|
+
const titleProps =
|
|
232
|
+
activePage !== null && activePage !== undefined
|
|
233
|
+
? { title: activePage.title, titleIcon: activePage.icon }
|
|
234
|
+
: { title: "Home", titleIcon: "HouseIcon" };
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<DevShell
|
|
238
|
+
state={state}
|
|
239
|
+
pathname={state.pathname}
|
|
240
|
+
sidebarOpen={state.sidebarOpen}
|
|
241
|
+
{...titleProps}
|
|
242
|
+
>
|
|
243
|
+
{activePage ? (
|
|
244
|
+
<PageView pageKey={activePage.key} bump={bump} />
|
|
245
|
+
) : pageKey ? (
|
|
246
|
+
<div class="p-6 text-xs text-muted-foreground">
|
|
247
|
+
Page <code class="font-mono">{pageKey}</code> not declared.
|
|
248
|
+
</div>
|
|
249
|
+
) : (
|
|
250
|
+
<HomeView routers={state.routers} widgets={state.widgets} bump={bump} />
|
|
251
|
+
)}
|
|
252
|
+
</DevShell>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const root = document.getElementById("root");
|
|
257
|
+
if (!root) throw new Error("#root missing");
|
|
258
|
+
render(<DevLanding state={readState()} />, root);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Iframe runtime barrel. esbuild bundles this into one file
|
|
2
|
+
// (runtime.js) and the iframe importmap maps every external the
|
|
3
|
+
// surface bundle declares — `@yourrealm/sdk`, `@yourrealm/sdk/ui`,
|
|
4
|
+
// `@yourrealm/sdk/preact`, `@yourrealm/sdk/jsx-runtime`,
|
|
5
|
+
// `@yourrealm/sdk/services`, `zod` — to that one URL. The browser
|
|
6
|
+
// dedupes by URL, so every import shares one runtime instance.
|
|
7
|
+
|
|
8
|
+
export { Fragment, h, render } from "preact";
|
|
9
|
+
export type { ComponentChildren, JSX, VNode } from "preact";
|
|
10
|
+
|
|
11
|
+
export { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "preact/hooks";
|
|
12
|
+
|
|
13
|
+
// JSX automatic runtime. Surface bundles compiled with
|
|
14
|
+
// --jsx=automatic --jsx-import-source=@yourrealm/sdk emit
|
|
15
|
+
// `import { jsx, jsxs, Fragment } from "@yourrealm/sdk/jsx-runtime"`
|
|
16
|
+
// for every JSX element, and the importmap routes that specifier here.
|
|
17
|
+
// `Fragment` is the same identity as preact's (exported above) so it's
|
|
18
|
+
// reachable through both specifiers without a duplicate binding.
|
|
19
|
+
export { jsx, jsxs, jsxDEV } from "preact/jsx-runtime";
|
|
20
|
+
|
|
21
|
+
// Identity helpers from @yourrealm/sdk/ui — they exist for TS inference
|
|
22
|
+
// at authoring time, but the bundled surface calls them at module load,
|
|
23
|
+
// so the importmap-resolved runtime needs them as real functions too.
|
|
24
|
+
// Re-exported from ui-surface so typed and runtime versions can't drift.
|
|
25
|
+
export { action, loader, page, widget } from "../ui-surface.ts";
|
|
26
|
+
|
|
27
|
+
// Components — must mirror the type-only contract in
|
|
28
|
+
// `packages/sdk/src/ui/index.ts` (the surface bundle resolves names
|
|
29
|
+
// from that type list against this module's exports).
|
|
30
|
+
export * from "../ui/components/badge.tsx";
|
|
31
|
+
export * from "../ui/components/button.tsx";
|
|
32
|
+
export * from "../ui/components/card.tsx";
|
|
33
|
+
export * from "../ui/components/checkbox.tsx";
|
|
34
|
+
export * from "../ui/components/combobox.tsx";
|
|
35
|
+
export * from "../ui/components/dialog.tsx";
|
|
36
|
+
export * from "../ui/components/empty-state.tsx";
|
|
37
|
+
export * from "../ui/components/icons.tsx";
|
|
38
|
+
export * from "../ui/components/input.tsx";
|
|
39
|
+
export * from "../ui/components/label.tsx";
|
|
40
|
+
export * from "../ui/components/line-chart.tsx";
|
|
41
|
+
export * from "../ui/components/page.tsx";
|
|
42
|
+
export * from "../ui/components/pie-chart.tsx";
|
|
43
|
+
export * from "../ui/components/separator.tsx";
|
|
44
|
+
export * from "../ui/components/sheet.tsx";
|
|
45
|
+
export * from "../ui/components/skeleton.tsx";
|
|
46
|
+
export * from "../ui/components/switch.tsx";
|
|
47
|
+
export * from "../ui/components/table.tsx";
|
|
48
|
+
export * from "../ui/components/timeago.tsx";
|
|
49
|
+
export * from "../ui/components/tooltip.tsx";
|
|
50
|
+
|
|
51
|
+
// SDK + zod re-exports. Surface bundles externalize `@yourrealm/sdk`
|
|
52
|
+
// (`createApp`, `r`, `z`, `apply`, `interpolate`, `ref`) and `zod`
|
|
53
|
+
// (`z`, schemas) so a per-surface bundle is single-digit KB instead
|
|
54
|
+
// of ~414 KB. Both modules are browser-safe: plain authoring JS, with
|
|
55
|
+
// nothing that touches Deno or node.
|
|
56
|
+
export * from "../index.ts";
|
|
57
|
+
export * from "zod";
|
|
58
|
+
|
|
59
|
+
// `@yourrealm/sdk/docker` (registry-gate helpers) is referenced by
|
|
60
|
+
// realm.ts files at module load — `verifyAnonymousRequest` closes over
|
|
61
|
+
// `checkDockerBasic` / `dockerChallenge` even though only Home's gate
|
|
62
|
+
// sandbox calls them. The iframe never runs the gate, but the surface
|
|
63
|
+
// bundle imports the names at module load, so the importmap-resolved
|
|
64
|
+
// runtime must provide them. They're pure JS (no Deno/atob/Web Crypto —
|
|
65
|
+
// see docker.ts), so re-export the real fns.
|
|
66
|
+
export { checkDockerBasic, dockerChallenge } from "../docker.ts";
|
|
67
|
+
|
|
68
|
+
// `@yourrealm/sdk/services` (Postgres, Valkey, etc.) is referenced by
|
|
69
|
+
// realm.ts files at module load — `getDesiredState` closes over the
|
|
70
|
+
// classes even though only Home eval calls it. The iframe never
|
|
71
|
+
// instantiates these, so the runtime needs *names* but not behavior:
|
|
72
|
+
// a single empty class re-exported as every Service satisfies ESM
|
|
73
|
+
// resolution, and constructing one yields a no-op object.
|
|
74
|
+
class _IframeServiceStub {}
|
|
75
|
+
export {
|
|
76
|
+
_IframeServiceStub as Postgres,
|
|
77
|
+
_IframeServiceStub as MySQL,
|
|
78
|
+
_IframeServiceStub as Valkey,
|
|
79
|
+
_IframeServiceStub as Redis,
|
|
80
|
+
_IframeServiceStub as Garage,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export { mount, mountNeedsConfig } from "./mount.ts";
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Browser entry for `realm-sdk dev`'s `/install` route. Mounts the shared
|
|
2
|
+
// `DevShell` (Home-style sidebar) around the install panel so the page
|
|
3
|
+
// matches the rest of the dev product chrome bit-for-bit. esbuild
|
|
4
|
+
// bundles this into `install-panel.js`; the dev server's HTML shell
|
|
5
|
+
// loads it and reads two JSON script tags (the dev shell state +
|
|
6
|
+
// the install config).
|
|
7
|
+
//
|
|
8
|
+
// The panel doesn't talk to the dev server directly — it goes through
|
|
9
|
+
// an InstallAdapter that POSTs to `/install/api/preview` and
|
|
10
|
+
// `/install/api/install`. On failure those endpoints respond with the
|
|
11
|
+
// same wire shape Home uses (`{error, violations?}`), so the same
|
|
12
|
+
// `parseInstallError` helper rehydrates an InstallErrorShape for the
|
|
13
|
+
// panel to render.
|
|
14
|
+
|
|
15
|
+
import { render } from "preact";
|
|
16
|
+
import InstallAppPanel from "../ui/install/install-app-panel.tsx";
|
|
17
|
+
import { parseInstallError } from "../ui/install/install-error.tsx";
|
|
18
|
+
import {
|
|
19
|
+
InstallAdapterError,
|
|
20
|
+
type InstallAdapter,
|
|
21
|
+
type InstallPayload,
|
|
22
|
+
type PreviewResult,
|
|
23
|
+
} from "../ui/install/adapter.ts";
|
|
24
|
+
import { WORKER_CAPABILITIES } from "../permissions.ts";
|
|
25
|
+
import { DevShell, type DevShellState } from "../ui/dev/dev-shell.tsx";
|
|
26
|
+
|
|
27
|
+
// realm-sdk dev runs every service via docker compose on the operator's
|
|
28
|
+
// host, so the local environment effectively satisfies every host
|
|
29
|
+
// capability. Advertise the full set under a synthetic worker so the
|
|
30
|
+
// install panel doesn't flag every `host.*` grant as unsupported.
|
|
31
|
+
const DEV_WORKER = {
|
|
32
|
+
id: "dev",
|
|
33
|
+
slug: "dev",
|
|
34
|
+
name: "realm-sdk dev (local)",
|
|
35
|
+
capabilities: [...WORKER_CAPABILITIES],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
interface InstallConfig {
|
|
39
|
+
initialSource: string;
|
|
40
|
+
hideDeployTargets: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface InstallShellState extends DevShellState {
|
|
44
|
+
pathname: string;
|
|
45
|
+
sidebarOpen: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function readConfig(): InstallConfig {
|
|
49
|
+
const el = document.getElementById("install-config");
|
|
50
|
+
if (!el || el.tagName !== "SCRIPT") throw new Error("install-config script tag missing");
|
|
51
|
+
const parsed = JSON.parse(el.textContent ?? "{}") as Partial<InstallConfig>;
|
|
52
|
+
return {
|
|
53
|
+
initialSource: parsed.initialSource ?? "",
|
|
54
|
+
hideDeployTargets: parsed.hideDeployTargets ?? true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function readShellState(): InstallShellState {
|
|
59
|
+
const el = document.getElementById("dev-state");
|
|
60
|
+
if (!el || el.tagName !== "SCRIPT") throw new Error("dev-state script tag missing");
|
|
61
|
+
return JSON.parse(el.textContent ?? "{}") as InstallShellState;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function postJson(path: string, body: unknown): Promise<unknown> {
|
|
65
|
+
const res = await fetch(path, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: { "content-type": "application/json" },
|
|
68
|
+
body: JSON.stringify(body),
|
|
69
|
+
});
|
|
70
|
+
const data = await res.json().catch(() => null);
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
throw new InstallAdapterError(parseInstallError(res.status, data));
|
|
73
|
+
}
|
|
74
|
+
return data;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const devInstallAdapter: InstallAdapter = {
|
|
78
|
+
async preview(source: string): Promise<PreviewResult> {
|
|
79
|
+
return (await postJson("/install/api/preview", { source })) as PreviewResult;
|
|
80
|
+
},
|
|
81
|
+
async install(payload: InstallPayload): Promise<{ id: string }> {
|
|
82
|
+
const data = await postJson("/install/api/install", payload);
|
|
83
|
+
return { id: (data as { id?: string } | null)?.id ?? "" };
|
|
84
|
+
},
|
|
85
|
+
// No listSilos / createSilo: a Silo is a Home record on a named worker and
|
|
86
|
+
// dev has neither. Leaving them off is how the panel knows to skip the picker
|
|
87
|
+
// and its install gate, so a silo app reaches the dev compiler's own "no Silo
|
|
88
|
+
// in the fixture" error instead of a permanently disabled button.
|
|
89
|
+
// Dev mounts the app's own UI in place, so the id the panel passes on install
|
|
90
|
+
// is ignored and the redirect lands on the dev home.
|
|
91
|
+
onInstalled(): void {
|
|
92
|
+
globalThis.location.href = "/";
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
function InstallPage() {
|
|
97
|
+
const config = readConfig();
|
|
98
|
+
const shell = readShellState();
|
|
99
|
+
return (
|
|
100
|
+
<DevShell
|
|
101
|
+
state={shell}
|
|
102
|
+
pathname={shell.pathname}
|
|
103
|
+
sidebarOpen={shell.sidebarOpen}
|
|
104
|
+
title="Install"
|
|
105
|
+
titleIcon="DownloadSimpleIcon"
|
|
106
|
+
>
|
|
107
|
+
<InstallAppPanel
|
|
108
|
+
workers={[DEV_WORKER]}
|
|
109
|
+
plumbing={devInstallAdapter}
|
|
110
|
+
initialSource={config.initialSource}
|
|
111
|
+
hideDeployTargets={config.hideDeployTargets}
|
|
112
|
+
/>
|
|
113
|
+
</DevShell>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const root = document.getElementById("root");
|
|
118
|
+
if (!root) throw new Error("#root missing");
|
|
119
|
+
render(<InstallPage />, root);
|