node-app-build 6.1.1

Mini app developer CLI: scaffold, validate, package node-app-* Debian packages
import { useEffect, useState } from "react";

type AppState = "waiting_for_token" | "ready";

/**
 * Minimal iframe handshake skeleton for a platform-loaded UI.
 *
 *   1. UI mounts → posts `{ type: "ready" }` to window.parent.
 *   2. Platform replies with `{ type: "auth_token", token, expires_at }`.
 *   3. UI uses the token as `Authorization: Bearer ${token}` for any
 *      backend fetches against `/api/v2/node-apps/{{name}}/api/...`.
 *
 * The platform refreshes the token by posting another `auth_token` before
 * expiry — keep the listener mounted for the lifetime of the app.
 */
export default function App() {
  const [state, setState] = useState<AppState>("waiting_for_token");
  const [token, setToken] = useState<string | null>(null);

  useEffect(() => {
    function handleMessage(event: MessageEvent) {
      if (event.origin !== window.location.origin) return;
      const msg = event.data;
      if (!msg || typeof msg !== "object") return;
      if (msg.type === "auth_token" && msg.token) {
        setToken(msg.token);
        setState("ready");
      }
    }
    window.addEventListener("message", handleMessage);
    window.parent.postMessage({ type: "ready" }, window.location.origin);
    return () => window.removeEventListener("message", handleMessage);
  }, []);

  return (
    <main style={{ fontFamily: "system-ui, sans-serif", padding: 24 }}>
      <h1 style={{ fontSize: 18, margin: 0 }}>{"{{name}}"}</h1>
      <p style={{ color: "#666", marginTop: 8 }}>
        {state === "waiting_for_token"
          ? "Authorizing…"
          : `Authorized (token …${token?.slice(-6) ?? ""})`}
      </p>
    </main>
  );
}