@burdenoff/microfe-hookmovies 2026.716.1 → 2026.719.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.
|
@@ -140,7 +140,7 @@ var M = [
|
|
|
140
140
|
{
|
|
141
141
|
title: "Point at the GraphQL endpoint",
|
|
142
142
|
body: /* @__PURE__ */ A(O, { children: [
|
|
143
|
-
"All HookMovies data is served from a single
|
|
143
|
+
"All HookMovies data is served from a single GraphQL API. The endpoint is shown in the ",
|
|
144
144
|
/* @__PURE__ */ k("strong", { children: "GraphQL API" }),
|
|
145
145
|
" tab."
|
|
146
146
|
] })
|
|
@@ -236,7 +236,7 @@ var M = [
|
|
|
236
236
|
/* @__PURE__ */ k(z, {
|
|
237
237
|
icon: v,
|
|
238
238
|
title: "GraphQL API",
|
|
239
|
-
children: "One
|
|
239
|
+
children: "One GraphQL endpoint serves the entire HookMovies domain. Send POST requests with your API key as a bearer token."
|
|
240
240
|
}),
|
|
241
241
|
/* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("h3", {
|
|
242
242
|
className: "mb-2 text-sm font-semibold text-text-primary",
|
|
@@ -778,7 +778,7 @@ var M = [
|
|
|
778
778
|
return /* @__PURE__ */ A(n, {
|
|
779
779
|
title: "Developer Portal",
|
|
780
780
|
description: "Build on the HookMovies platform API",
|
|
781
|
-
purpose: "Everything you need to integrate with HookMovies — authenticate with an API key, query the
|
|
781
|
+
purpose: "Everything you need to integrate with HookMovies — authenticate with an API key, query the GraphQL gateway, pull in the official SDKs or MCP server, and embed the player anywhere. API keys are issued through the platform identity service; HookMovies never stores your secret.",
|
|
782
782
|
children: [/* @__PURE__ */ k("div", {
|
|
783
783
|
className: "mb-6 flex gap-1 overflow-x-auto rounded-xl border border-border-seam bg-bg-surface p-1",
|
|
784
784
|
role: "tablist",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DevPortalPage.js","names":[],"sources":["../../../../src/hookmovies/pages/developer/DevPortalPage.tsx"],"sourcesContent":["/**\n * Developer Portal\n * @module hookmovies/pages/developer\n *\n * Thin, product-branded developer surface (~95% reuse). It is documentation +\n * a live API-key manager, NOT a new key store: the keys panel reuses the\n * platform's global-auth service verbatim through the shared data-layer hooks\n * (`useMyApiKeys` / `useGenerateApiKey` / `useRevokeApiKey`, all on the GLOBAL\n * gateway). The rest of the page is static, tokenized reference content:\n *\n * - Getting Started — how to authenticate and issue a first request.\n * - GraphQL API — the real workspace gateway endpoint plus a runnable\n * `hokListTitles` query / response sample.\n * - API Keys — list, mint (secret shown exactly once) and revoke.\n * - SDKs & MCP — links to the real HookMovies packages.\n * - Embeddable Player — a copy-paste <iframe> embed for `/hookmovies/watch/:id`.\n */\n\nimport { type FC, type ReactNode, type FormEvent, useEffect, useMemo, useState } from 'react';\nimport {\n Rocket,\n Code2,\n KeyRound,\n Package,\n Clapperboard,\n Copy,\n Check,\n Plus,\n Trash2,\n ShieldAlert,\n ExternalLink,\n Terminal,\n BookOpen,\n Boxes,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { useHookMovies } from '../../context/HookMoviesContext';\nimport {\n useMyApiKeys,\n useGenerateApiKey,\n useRevokeApiKey,\n type ApiKey,\n type ApiKeyResult,\n} from '../../hooks/useHookMoviesApi';\nimport { formatDate, formatRelativeTime } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\n\n// ============================================================================\n// Section model\n// ============================================================================\n\ntype SectionId = 'overview' | 'graphql' | 'keys' | 'sdks' | 'embed';\n\ninterface SectionMeta {\n id: SectionId;\n label: string;\n icon: typeof Rocket;\n}\n\nconst SECTIONS: SectionMeta[] = [\n { id: 'overview', label: 'Getting Started', icon: Rocket },\n { id: 'graphql', label: 'GraphQL API', icon: Code2 },\n { id: 'keys', label: 'API Keys', icon: KeyRound },\n { id: 'sdks', label: 'SDKs & MCP', icon: Package },\n { id: 'embed', label: 'Embeddable Player', icon: Clapperboard },\n];\n\n/** API-key scopes offered by the mint form (global-auth scopes are free-form strings). */\nconst SCOPE_OPTIONS: { value: string; label: string; description: string }[] = [\n { value: 'read', label: 'Read', description: 'Query the catalog, titles, reviews and metadata.' },\n {\n value: 'write',\n label: 'Write',\n description: 'Create and update titles, reviews and other resources.',\n },\n];\n\n// ============================================================================\n// Copy-to-clipboard button\n// ============================================================================\n\nconst useCopy = (): [boolean, (text: string) => void] => {\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n if (!copied) return;\n const timer = window.setTimeout(() => setCopied(false), 1800);\n return () => window.clearTimeout(timer);\n }, [copied]);\n\n const copy = (text: string) => {\n if (typeof navigator === 'undefined' || !navigator.clipboard) return;\n void navigator.clipboard.writeText(text).then(\n () => setCopied(true),\n () => setCopied(false)\n );\n };\n\n return [copied, copy];\n};\n\nconst CopyButton: FC<{ value: string; label?: string; className?: string }> = ({\n value,\n label,\n className,\n}) => {\n const [copied, copy] = useCopy();\n return (\n <button\n type=\"button\"\n onClick={() => copy(value)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border border-border-seam bg-bg-surface px-2.5 py-1 text-xs font-medium text-text-secondary transition-colors hover:border-border-strong hover:text-text-primary',\n className\n )}\n aria-label={copied ? 'Copied' : `Copy ${label ?? 'to clipboard'}`}\n >\n {copied ? (\n <Check className=\"h-3.5 w-3.5 text-status-success-text\" aria-hidden=\"true\" />\n ) : (\n <Copy className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n )}\n {copied ? 'Copied' : (label ?? 'Copy')}\n </button>\n );\n};\n\n// ============================================================================\n// Tokenized code block (semantic tokens only — no raw colours)\n// ============================================================================\n\n/**\n * Lightweight, allocation-safe syntax highlighter. A single ordered alternation\n * classifies comments, strings, numbers and a small keyword set (GraphQL / shell\n * / JS) and maps each class onto a fe-libs semantic colour token. Everything else\n * renders as `text-text-primary`, so the block degrades gracefully for any input.\n */\nconst HIGHLIGHT_RE =\n /(#[^\\n]*|\\/\\/[^\\n]*)|(\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'|`(?:[^`\\\\]|\\\\.)*`)|(\\b\\d+(?:\\.\\d+)?\\b)|(\\b(?:query|mutation|subscription|fragment|type|input|enum|schema|on|true|false|null|const|let|var|import|from|export|default|function|return|await|async|new|curl|POST|GET|Authorization|Bearer|Content-Type)\\b)/g;\n\nconst highlightCode = (code: string): ReactNode[] => {\n const nodes: ReactNode[] = [];\n let lastIndex = 0;\n let key = 0;\n for (const match of code.matchAll(HIGHLIGHT_RE)) {\n const index = match.index ?? 0;\n if (index > lastIndex) {\n nodes.push(<span key={key++}>{code.slice(lastIndex, index)}</span>);\n }\n const [full, comment, str, num, keyword] = match;\n if (comment) {\n nodes.push(\n <span key={key++} className=\"text-text-muted italic\">\n {full}\n </span>\n );\n } else if (str) {\n nodes.push(\n <span key={key++} className=\"text-status-success-text\">\n {full}\n </span>\n );\n } else if (num) {\n nodes.push(\n <span key={key++} className=\"text-status-warning-text\">\n {full}\n </span>\n );\n } else if (keyword) {\n nodes.push(\n <span key={key++} className=\"text-status-info-text font-medium\">\n {full}\n </span>\n );\n } else {\n nodes.push(<span key={key++}>{full}</span>);\n }\n lastIndex = index + full.length;\n }\n if (lastIndex < code.length) {\n nodes.push(<span key={key++}>{code.slice(lastIndex)}</span>);\n }\n return nodes;\n};\n\nconst CodeBlock: FC<{ code: string; language: string; className?: string }> = ({\n code,\n language,\n className,\n}) => (\n <div\n className={cn('overflow-hidden rounded-xl border border-border-seam bg-bg-sunken', className)}\n >\n <div className=\"flex items-center justify-between border-b border-border-seam bg-bg-surface px-3 py-1.5\">\n <span className=\"inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-text-muted\">\n <Terminal className=\"h-3 w-3\" aria-hidden=\"true\" />\n {language}\n </span>\n <CopyButton value={code} label=\"Copy\" />\n </div>\n <div className=\"overflow-x-auto\">\n <pre className=\"min-w-0 px-4 py-3 text-[13px] leading-relaxed\">\n <code className=\"font-mono text-text-primary\">{highlightCode(code)}</code>\n </pre>\n </div>\n </div>\n);\n\n// ============================================================================\n// Small presentational helpers\n// ============================================================================\n\nconst SectionHeading: FC<{ icon: typeof Rocket; title: string; children?: ReactNode }> = ({\n icon: Icon,\n title,\n children,\n}) => (\n <div className=\"mb-4\">\n <h2 className=\"flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Icon className=\"h-5 w-5 text-action-primary-bg\" aria-hidden=\"true\" />\n {title}\n </h2>\n {children && <p className=\"mt-1 max-w-2xl text-sm text-text-secondary\">{children}</p>}\n </div>\n);\n\nconst InlineCode: FC<{ children: ReactNode }> = ({ children }) => (\n <code className=\"rounded bg-bg-sunken px-1.5 py-0.5 font-mono text-[0.85em] text-text-primary\">\n {children}\n </code>\n);\n\n// ============================================================================\n// Getting Started\n// ============================================================================\n\nconst GETTING_STARTED_STEPS: { title: string; body: ReactNode }[] = [\n {\n title: 'Create an API key',\n body: (\n <>\n Head to the <strong>API Keys</strong> tab and mint a key for your registered app. The secret\n is shown once at creation — copy it somewhere safe.\n </>\n ),\n },\n {\n title: 'Point at the GraphQL endpoint',\n body: (\n <>\n All HookMovies data is served from a single Apollo Federation gateway. The endpoint is shown\n in the <strong>GraphQL API</strong> tab.\n </>\n ),\n },\n {\n title: 'Authenticate every request',\n body: (\n <>\n Send your key as a bearer token in the <InlineCode>Authorization</InlineCode> header:{' '}\n <InlineCode>Authorization: Bearer <apiSecret></InlineCode>.\n </>\n ),\n },\n {\n title: 'Make your first query',\n body: (\n <>\n Call <InlineCode>hokListTitles</InlineCode> to page through the catalog, then drill into a\n title with <InlineCode>hokTitle(id)</InlineCode>.\n </>\n ),\n },\n];\n\nconst GettingStartedSection: FC<{ endpoint: string }> = ({ endpoint }) => (\n <div className=\"space-y-6\">\n <SectionHeading icon={Rocket} title=\"Getting Started\">\n Build with the HookMovies platform API — a single GraphQL gateway that powers the catalog,\n productions, auditions, entitlements and playback surfaces you see in this app.\n </SectionHeading>\n\n <ol className=\"grid gap-4 sm:grid-cols-2\">\n {GETTING_STARTED_STEPS.map((step, i) => (\n <li key={step.title}>\n <GlassCard treatment=\"glass\" className=\"h-full border-border-seam p-4\">\n <div className=\"flex items-start gap-3\">\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-action-primary-bg text-sm font-semibold text-action-primary-text\">\n {i + 1}\n </span>\n <div className=\"min-w-0\">\n <h3 className=\"text-sm font-semibold text-text-primary\">{step.title}</h3>\n <p className=\"mt-1 text-sm text-text-secondary\">{step.body}</p>\n </div>\n </div>\n </GlassCard>\n </li>\n ))}\n </ol>\n\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-4\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <BookOpen className=\"h-4 w-4 text-action-primary-bg\" aria-hidden=\"true\" />\n Base endpoint\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Every request targets the workspace GraphQL gateway:\n </p>\n <div className=\"mt-3 flex flex-wrap items-center gap-2\">\n <code className=\"min-w-0 flex-1 overflow-x-auto rounded-lg border border-border-seam bg-bg-sunken px-3 py-2 font-mono text-sm text-text-primary\">\n {endpoint}\n </code>\n <CopyButton value={endpoint} label=\"Copy URL\" />\n </div>\n </GlassCard>\n </div>\n);\n\n// ============================================================================\n// GraphQL API\n// ============================================================================\n\nconst SAMPLE_QUERY = `query FirstTitles {\n hokListTitles(pagination: { limit: 10, offset: 0 }, status: PUBLISHED) {\n items {\n id\n title\n type\n status\n releaseYear\n genres\n }\n pageInfo { cursor hasMore }\n totalCount\n }\n}`;\n\nconst SAMPLE_RESPONSE = `{\n \"data\": {\n \"hokListTitles\": {\n \"items\": [\n {\n \"id\": \"9f2c…\",\n \"title\": \"Midnight in Marseille\",\n \"type\": \"MOVIE\",\n \"status\": \"PUBLISHED\",\n \"releaseYear\": 2025,\n \"genres\": [\"Drama\", \"Thriller\"]\n }\n ],\n \"pageInfo\": { \"cursor\": null, \"hasMore\": true },\n \"totalCount\": 128\n }\n }\n}`;\n\nconst GraphqlSection: FC<{ endpoint: string }> = ({ endpoint }) => {\n const curl = `curl -X POST '${endpoint}' \\\\\n -H 'Content-Type: application/json' \\\\\n -H 'Authorization: Bearer <apiSecret>' \\\\\n -d '{\"query\": \"query { hokListTitles(pagination: { limit: 5 }) { items { id title } totalCount } }\"}'`;\n\n return (\n <div className=\"space-y-6\">\n <SectionHeading icon={Code2} title=\"GraphQL API\">\n One federated GraphQL endpoint serves the entire HookMovies domain. Send POST requests with\n your API key as a bearer token.\n </SectionHeading>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Endpoint</h3>\n <div className=\"flex flex-wrap items-center gap-2\">\n <code className=\"min-w-0 flex-1 overflow-x-auto rounded-lg border border-border-seam bg-bg-sunken px-3 py-2 font-mono text-sm text-text-primary\">\n {endpoint}\n </code>\n <CopyButton value={endpoint} label=\"Copy URL\" />\n </div>\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Sample query</h3>\n <CodeBlock language=\"graphql\" code={SAMPLE_QUERY} />\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Sample response</h3>\n <CodeBlock language=\"json\" code={SAMPLE_RESPONSE} />\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Run it with cURL</h3>\n <CodeBlock language=\"bash\" code={curl} />\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// API Keys — reuses global-auth via the shared data-layer hooks\n// ============================================================================\n\nconst SecretReveal: FC<{ result: ApiKeyResult; onDismiss: () => void }> = ({\n result,\n onDismiss,\n}) => (\n <GlassCard\n treatment=\"glass\"\n className=\"mb-6 border-status-warning-border bg-status-warning-bg-subtle p-5\"\n >\n <div className=\"flex items-start gap-3\">\n <ShieldAlert\n className=\"mt-0.5 h-5 w-5 shrink-0 text-status-warning-text\"\n aria-hidden=\"true\"\n />\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Save your API secret now</h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n This secret is shown only once and cannot be retrieved again. Store it somewhere safe\n before dismissing this panel.\n </p>\n\n <dl className=\"mt-4 space-y-3\">\n {(\n [\n { label: 'API key', value: result.apiKey },\n { label: 'API secret', value: result.apiSecret },\n { label: 'Key ID', value: result.keyId },\n ] as const\n ).map((row) => (\n <div key={row.label}>\n <dt className=\"mb-1 text-xs font-medium text-text-secondary\">{row.label}</dt>\n <dd className=\"flex flex-wrap items-center gap-2\">\n <code className=\"min-w-0 flex-1 overflow-x-auto rounded-lg border border-border-seam bg-bg-surface px-3 py-2 font-mono text-sm text-text-primary\">\n {row.value}\n </code>\n <CopyButton value={row.value} label=\"Copy\" />\n </dd>\n </div>\n ))}\n </dl>\n\n <button\n type=\"button\"\n onClick={onDismiss}\n className=\"mt-4 rounded-lg border border-border-seam bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n I've saved it — dismiss\n </button>\n </div>\n </div>\n </GlassCard>\n);\n\nconst fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelClass = 'mb-1.5 block text-xs font-medium text-text-secondary';\n\nconst GenerateKeyForm: FC<{\n onGenerated: (result: ApiKeyResult) => void;\n onClose: () => void;\n}> = ({ onGenerated, onClose }) => {\n const generate = useGenerateApiKey();\n const [name, setName] = useState('');\n const [appId, setAppId] = useState('');\n const [scopes, setScopes] = useState<string[]>(['read']);\n const [expiresAt, setExpiresAt] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const toggleScope = (value: string) => {\n setScopes((prev) =>\n prev.includes(value) ? prev.filter((s) => s !== value) : [...prev, value]\n );\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!name.trim()) {\n setError('Give the key a recognisable name.');\n return;\n }\n if (!appId.trim()) {\n setError('Enter the App ID this key will act on behalf of.');\n return;\n }\n if (scopes.length === 0) {\n setError('Select at least one scope.');\n return;\n }\n try {\n const result = await generate.mutateAsync({\n appId: appId.trim(),\n name: name.trim(),\n scope: scopes,\n expiresAt: expiresAt ? new Date(expiresAt).toISOString() : undefined,\n });\n onGenerated(result);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to generate the API key.');\n }\n };\n\n return (\n <GlassCard treatment=\"glass\" className=\"mb-6 border-border-seam p-5\">\n <h3 className=\"mb-4 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <KeyRound className=\"h-4 w-4 text-action-primary-bg\" aria-hidden=\"true\" />\n Generate a new API key\n </h3>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"key-name\" className={labelClass}>\n Key name *\n </label>\n <input\n id=\"key-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Production ingest\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"key-app-id\" className={labelClass}>\n App ID *\n </label>\n <input\n id=\"key-app-id\"\n value={appId}\n onChange={(e) => setAppId(e.target.value)}\n placeholder=\"Your registered app's ID\"\n className={fieldClass}\n required\n />\n <p className=\"mt-1 text-[11px] text-text-muted\">\n The client app this key acts on behalf of — find it in your workspace app settings.\n </p>\n </div>\n </div>\n\n <fieldset>\n <legend className={labelClass}>Scopes *</legend>\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-2\">\n {SCOPE_OPTIONS.map((scope) => {\n const checked = scopes.includes(scope.value);\n return (\n <label\n key={scope.value}\n className={cn(\n 'flex cursor-pointer items-start gap-2 rounded-lg border p-3 text-sm transition-colors',\n checked\n ? 'border-action-primary-bg bg-action-primary-bg/10'\n : 'border-border-subtle hover:border-border-strong'\n )}\n >\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={() => toggleScope(scope.value)}\n className=\"mt-0.5 accent-[var(--color-action-primary-bg)]\"\n />\n <span className=\"min-w-0\">\n <span className=\"block font-medium text-text-primary\">{scope.label}</span>\n <span className=\"block text-xs text-text-secondary\">{scope.description}</span>\n </span>\n </label>\n );\n })}\n </div>\n </fieldset>\n\n <div>\n <label htmlFor=\"key-expires\" className={labelClass}>\n Expires (optional)\n </label>\n <input\n id=\"key-expires\"\n type=\"date\"\n value={expiresAt}\n onChange={(e) => setExpiresAt(e.target.value)}\n className={cn(fieldClass, 'sm:max-w-xs')}\n />\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"submit\"\n disabled={generate.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50\"\n >\n {generate.isPending ? 'Generating…' : 'Generate key'}\n </button>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n Cancel\n </button>\n </div>\n </form>\n </GlassCard>\n );\n};\n\nconst ApiKeyRow: FC<{ apiKey: ApiKey }> = ({ apiKey }) => {\n const revoke = useRevokeApiKey();\n const [confirming, setConfirming] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleRevoke = async () => {\n setError(null);\n try {\n await revoke.mutateAsync(apiKey.id);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to revoke this key.');\n setConfirming(false);\n }\n };\n\n return (\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-text-primary\">{apiKey.name}</span>\n <code className=\"rounded bg-bg-sunken px-1.5 py-0.5 font-mono text-xs text-text-secondary\">\n {apiKey.prefix}…\n </code>\n </div>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-muted\">\n {apiKey.appName && <span>{apiKey.appName}</span>}\n <span aria-hidden=\"true\">·</span>\n <span>\n Created {formatDate(apiKey.createdAt, { hour: undefined, minute: undefined })}\n </span>\n <span aria-hidden=\"true\">·</span>\n <span>\n {apiKey.lastUsedAt\n ? `Last used ${formatRelativeTime(apiKey.lastUsedAt)}`\n : 'Never used'}\n </span>\n {apiKey.expiresAt && (\n <>\n <span aria-hidden=\"true\">·</span>\n <span>\n Expires {formatDate(apiKey.expiresAt, { hour: undefined, minute: undefined })}\n </span>\n </>\n )}\n </div>\n {apiKey.scopes.length > 0 && (\n <div className=\"mt-2 flex flex-wrap gap-1.5\">\n {apiKey.scopes.map((scope) => (\n <span\n key={scope}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-text-secondary\"\n >\n {scope}\n </span>\n ))}\n </div>\n )}\n </div>\n\n <div className=\"shrink-0\">\n {confirming ? (\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={revoke.isPending}\n onClick={() => void handleRevoke()}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-1.5 text-xs font-medium text-status-error-text transition-opacity hover:opacity-90 disabled:opacity-50\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {revoke.isPending ? 'Revoking…' : 'Confirm revoke'}\n </button>\n <button\n type=\"button\"\n onClick={() => setConfirming(false)}\n className=\"rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n Cancel\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => setConfirming(true)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:border-status-error-border hover:text-status-error-text\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Revoke\n </button>\n )}\n </div>\n </div>\n {error && <p className=\"mt-2 text-xs text-status-error-text\">{error}</p>}\n </GlassCard>\n );\n};\n\nconst ApiKeysSection: FC = () => {\n const { data, isLoading, isError, refetch } = useMyApiKeys();\n const [showForm, setShowForm] = useState(false);\n const [revealed, setRevealed] = useState<ApiKeyResult | null>(null);\n\n const keys = data?.apiKeys ?? [];\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <SectionHeading icon={KeyRound} title=\"API Keys\">\n Keys authenticate server-to-server calls to the HookMovies GraphQL gateway. They are\n issued and revoked through the platform identity service — HookMovies never stores your\n secret.\n </SectionHeading>\n <button\n type=\"button\"\n onClick={() => setShowForm((v) => !v)}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n New key\n </button>\n </div>\n\n {revealed && <SecretReveal result={revealed} onDismiss={() => setRevealed(null)} />}\n\n {showForm && (\n <GenerateKeyForm\n onGenerated={(result) => {\n setRevealed(result);\n setShowForm(false);\n }}\n onClose={() => setShowForm(false)}\n />\n )}\n\n {isLoading ? (\n <LoadingState label=\"Loading your API keys…\" />\n ) : isError ? (\n <ErrorState message=\"We couldn't load your API keys.\" onRetry={() => void refetch()} />\n ) : keys.length === 0 ? (\n <EmptyState\n icon={<KeyRound className=\"h-12 w-12\" />}\n title=\"No API keys yet\"\n description=\"Generate your first key to start calling the HookMovies API.\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowForm(true)}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n Generate key\n </button>\n }\n />\n ) : (\n <div className=\"space-y-3\">\n {keys.map((apiKey) => (\n <ApiKeyRow key={apiKey.id} apiKey={apiKey} />\n ))}\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// SDKs & MCP\n// ============================================================================\n\ninterface PackageCard {\n name: string;\n repo: string;\n description: string;\n icon: typeof Package;\n install: { language: string; code: string };\n}\n\nconst PACKAGES: PackageCard[] = [\n {\n name: 'Node SDK',\n repo: 'hookmovies-sdk-node',\n description:\n 'Typed TypeScript client for the HookMovies GraphQL API — catalog, titles, reviews and playback.',\n icon: Boxes,\n install: {\n language: 'bash',\n code: 'bun add hookmovies-sdk-node\\n# or: npm install hookmovies-sdk-node',\n },\n },\n {\n name: 'Python SDK',\n repo: 'hookmovies-sdk-python',\n description: 'Pythonic client with async support for backend integrations and data pipelines.',\n icon: Package,\n install: { language: 'bash', code: 'pip install hookmovies-sdk-python' },\n },\n {\n name: 'MCP Server',\n repo: 'hookmovies-mcp',\n description:\n 'Model Context Protocol server that exposes HookMovies tools to AI agents and assistants.',\n icon: Boxes,\n install: { language: 'bash', code: 'bunx hookmovies-mcp --help' },\n },\n {\n name: 'CLI',\n repo: 'hookmovies-cli',\n description:\n 'Command-line tool to manage titles, productions and rights from your terminal or CI.',\n icon: Terminal,\n install: { language: 'bash', code: 'bunx hookmovies-cli titles list' },\n },\n];\n\nconst SdksSection: FC = () => (\n <div className=\"space-y-6\">\n <SectionHeading icon={Package} title=\"SDKs & MCP\">\n Official HookMovies packages wrap the GraphQL API so you can build in your language of choice\n — or plug the platform straight into an AI agent via MCP.\n </SectionHeading>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n {PACKAGES.map((pkg) => {\n const Icon = pkg.icon;\n const repoUrl = `https://github.com/algoshred/${pkg.repo}`;\n return (\n <GlassCard\n key={pkg.repo}\n treatment=\"glass\"\n className=\"flex h-full flex-col border-border-seam p-4\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-lg bg-action-primary-bg/10\">\n <Icon className=\"h-5 w-5 text-action-primary-bg\" aria-hidden=\"true\" />\n </span>\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary\">{pkg.name}</h3>\n <code className=\"text-[11px] text-text-muted\">{pkg.repo}</code>\n </div>\n </div>\n <a\n href={repoUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1 rounded-md border border-border-seam px-2 py-1 text-xs font-medium text-text-secondary transition-colors hover:border-border-strong hover:text-text-primary\"\n >\n Repo\n <ExternalLink className=\"h-3 w-3\" aria-hidden=\"true\" />\n </a>\n </div>\n <p className=\"mt-3 flex-1 text-sm text-text-secondary\">{pkg.description}</p>\n <div className=\"mt-3\">\n <CodeBlock language={pkg.install.language} code={pkg.install.code} />\n </div>\n </GlassCard>\n );\n })}\n </div>\n </div>\n);\n\n// ============================================================================\n// Embeddable Player\n// ============================================================================\n\nconst EmbedSection: FC<{ watchUrl: string }> = ({ watchUrl }) => {\n const iframeSnippet = `<iframe\n src=\"${watchUrl}\"\n width=\"100%\"\n height=\"480\"\n frameborder=\"0\"\n allow=\"autoplay; fullscreen; encrypted-media\"\n allowfullscreen\n title=\"HookMovies player\"\n></iframe>`;\n\n const responsiveSnippet = `<div style=\"position: relative; padding-top: 56.25%;\">\n <iframe\n src=\"${watchUrl}\"\n style=\"position: absolute; inset: 0; width: 100%; height: 100%; border: 0;\"\n allow=\"autoplay; fullscreen; encrypted-media\"\n allowfullscreen\n title=\"HookMovies player\"\n ></iframe>\n</div>`;\n\n return (\n <div className=\"space-y-6\">\n <SectionHeading icon={Clapperboard} title=\"Embeddable Player\">\n Drop a HookMovies title into any web page with a single{' '}\n <InlineCode><iframe></InlineCode>. Replace <InlineCode>YOUR_TITLE_ID</InlineCode> with\n the id of any published title.\n </SectionHeading>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Basic embed</h3>\n <CodeBlock language=\"html\" code={iframeSnippet} />\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Responsive (16:9) embed</h3>\n <p className=\"mb-2 text-sm text-text-secondary\">\n Wrap the iframe in an aspect-ratio box so the player scales fluidly on any device.\n </p>\n <CodeBlock language=\"html\" code={responsiveSnippet} />\n </div>\n\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-4\">\n <p className=\"text-sm text-text-secondary\">\n The embedded player respects the viewer's entitlements and profile preferences. Viewers\n who aren't entitled to a title are prompted to sign in or purchase access inside the\n frame.\n </p>\n </GlassCard>\n </div>\n );\n};\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport const DevPortalPage: FC = () => {\n const { apiGatewayUrl, getPath } = useHookMovies();\n const [active, setActive] = useState<SectionId>('overview');\n\n const endpoint = apiGatewayUrl || 'https://graphqlworkspaces.burdenoff.com/workspaces/graphql';\n\n const watchUrl = useMemo(() => {\n const origin =\n typeof window !== 'undefined' ? window.location.origin : 'https://app.hookmovies.com';\n return `${origin}${getPath('watch/YOUR_TITLE_ID')}`;\n }, [getPath]);\n\n return (\n <PageLayout\n title=\"Developer Portal\"\n description=\"Build on the HookMovies platform API\"\n purpose=\"Everything you need to integrate with HookMovies — authenticate with an API key, query the federated GraphQL gateway, pull in the official SDKs or MCP server, and embed the player anywhere. API keys are issued through the platform identity service; HookMovies never stores your secret.\"\n >\n {/* Section navigation — horizontal, scrollable on small screens */}\n <div\n className=\"mb-6 flex gap-1 overflow-x-auto rounded-xl border border-border-seam bg-bg-surface p-1\"\n role=\"tablist\"\n aria-label=\"Developer portal sections\"\n >\n {SECTIONS.map((section) => {\n const Icon = section.icon;\n const selected = active === section.id;\n return (\n <button\n key={section.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={selected}\n onClick={() => setActive(section.id)}\n className={cn(\n 'inline-flex shrink-0 items-center gap-2 rounded-lg px-3.5 py-2 text-sm font-medium transition-colors',\n selected\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:text-text-primary'\n )}\n >\n <Icon className=\"h-4 w-4\" aria-hidden=\"true\" />\n {section.label}\n </button>\n );\n })}\n </div>\n\n <div className=\"max-w-4xl\">\n {active === 'overview' && <GettingStartedSection endpoint={endpoint} />}\n {active === 'graphql' && <GraphqlSection endpoint={endpoint} />}\n {active === 'keys' && <ApiKeysSection />}\n {active === 'sdks' && <SdksSection />}\n {active === 'embed' && <EmbedSection watchUrl={watchUrl} />}\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DevPortalPage.js","names":[],"sources":["../../../../src/hookmovies/pages/developer/DevPortalPage.tsx"],"sourcesContent":["/**\n * Developer Portal\n * @module hookmovies/pages/developer\n *\n * Thin, product-branded developer surface (~95% reuse). It is documentation +\n * a live API-key manager, NOT a new key store: the keys panel reuses the\n * platform's global-auth service verbatim through the shared data-layer hooks\n * (`useMyApiKeys` / `useGenerateApiKey` / `useRevokeApiKey`, all on the GLOBAL\n * gateway). The rest of the page is static, tokenized reference content:\n *\n * - Getting Started — how to authenticate and issue a first request.\n * - GraphQL API — the real workspace gateway endpoint plus a runnable\n * `hokListTitles` query / response sample.\n * - API Keys — list, mint (secret shown exactly once) and revoke.\n * - SDKs & MCP — links to the real HookMovies packages.\n * - Embeddable Player — a copy-paste <iframe> embed for `/hookmovies/watch/:id`.\n */\n\nimport { type FC, type ReactNode, type FormEvent, useEffect, useMemo, useState } from 'react';\nimport {\n Rocket,\n Code2,\n KeyRound,\n Package,\n Clapperboard,\n Copy,\n Check,\n Plus,\n Trash2,\n ShieldAlert,\n ExternalLink,\n Terminal,\n BookOpen,\n Boxes,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingState, ErrorState } from '../../components/StateViews';\nimport { useHookMovies } from '../../context/HookMoviesContext';\nimport {\n useMyApiKeys,\n useGenerateApiKey,\n useRevokeApiKey,\n type ApiKey,\n type ApiKeyResult,\n} from '../../hooks/useHookMoviesApi';\nimport { formatDate, formatRelativeTime } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\n\n// ============================================================================\n// Section model\n// ============================================================================\n\ntype SectionId = 'overview' | 'graphql' | 'keys' | 'sdks' | 'embed';\n\ninterface SectionMeta {\n id: SectionId;\n label: string;\n icon: typeof Rocket;\n}\n\nconst SECTIONS: SectionMeta[] = [\n { id: 'overview', label: 'Getting Started', icon: Rocket },\n { id: 'graphql', label: 'GraphQL API', icon: Code2 },\n { id: 'keys', label: 'API Keys', icon: KeyRound },\n { id: 'sdks', label: 'SDKs & MCP', icon: Package },\n { id: 'embed', label: 'Embeddable Player', icon: Clapperboard },\n];\n\n/** API-key scopes offered by the mint form (global-auth scopes are free-form strings). */\nconst SCOPE_OPTIONS: { value: string; label: string; description: string }[] = [\n { value: 'read', label: 'Read', description: 'Query the catalog, titles, reviews and metadata.' },\n {\n value: 'write',\n label: 'Write',\n description: 'Create and update titles, reviews and other resources.',\n },\n];\n\n// ============================================================================\n// Copy-to-clipboard button\n// ============================================================================\n\nconst useCopy = (): [boolean, (text: string) => void] => {\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n if (!copied) return;\n const timer = window.setTimeout(() => setCopied(false), 1800);\n return () => window.clearTimeout(timer);\n }, [copied]);\n\n const copy = (text: string) => {\n if (typeof navigator === 'undefined' || !navigator.clipboard) return;\n void navigator.clipboard.writeText(text).then(\n () => setCopied(true),\n () => setCopied(false)\n );\n };\n\n return [copied, copy];\n};\n\nconst CopyButton: FC<{ value: string; label?: string; className?: string }> = ({\n value,\n label,\n className,\n}) => {\n const [copied, copy] = useCopy();\n return (\n <button\n type=\"button\"\n onClick={() => copy(value)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border border-border-seam bg-bg-surface px-2.5 py-1 text-xs font-medium text-text-secondary transition-colors hover:border-border-strong hover:text-text-primary',\n className\n )}\n aria-label={copied ? 'Copied' : `Copy ${label ?? 'to clipboard'}`}\n >\n {copied ? (\n <Check className=\"h-3.5 w-3.5 text-status-success-text\" aria-hidden=\"true\" />\n ) : (\n <Copy className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n )}\n {copied ? 'Copied' : (label ?? 'Copy')}\n </button>\n );\n};\n\n// ============================================================================\n// Tokenized code block (semantic tokens only — no raw colours)\n// ============================================================================\n\n/**\n * Lightweight, allocation-safe syntax highlighter. A single ordered alternation\n * classifies comments, strings, numbers and a small keyword set (GraphQL / shell\n * / JS) and maps each class onto a fe-libs semantic colour token. Everything else\n * renders as `text-text-primary`, so the block degrades gracefully for any input.\n */\nconst HIGHLIGHT_RE =\n /(#[^\\n]*|\\/\\/[^\\n]*)|(\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'|`(?:[^`\\\\]|\\\\.)*`)|(\\b\\d+(?:\\.\\d+)?\\b)|(\\b(?:query|mutation|subscription|fragment|type|input|enum|schema|on|true|false|null|const|let|var|import|from|export|default|function|return|await|async|new|curl|POST|GET|Authorization|Bearer|Content-Type)\\b)/g;\n\nconst highlightCode = (code: string): ReactNode[] => {\n const nodes: ReactNode[] = [];\n let lastIndex = 0;\n let key = 0;\n for (const match of code.matchAll(HIGHLIGHT_RE)) {\n const index = match.index ?? 0;\n if (index > lastIndex) {\n nodes.push(<span key={key++}>{code.slice(lastIndex, index)}</span>);\n }\n const [full, comment, str, num, keyword] = match;\n if (comment) {\n nodes.push(\n <span key={key++} className=\"text-text-muted italic\">\n {full}\n </span>\n );\n } else if (str) {\n nodes.push(\n <span key={key++} className=\"text-status-success-text\">\n {full}\n </span>\n );\n } else if (num) {\n nodes.push(\n <span key={key++} className=\"text-status-warning-text\">\n {full}\n </span>\n );\n } else if (keyword) {\n nodes.push(\n <span key={key++} className=\"text-status-info-text font-medium\">\n {full}\n </span>\n );\n } else {\n nodes.push(<span key={key++}>{full}</span>);\n }\n lastIndex = index + full.length;\n }\n if (lastIndex < code.length) {\n nodes.push(<span key={key++}>{code.slice(lastIndex)}</span>);\n }\n return nodes;\n};\n\nconst CodeBlock: FC<{ code: string; language: string; className?: string }> = ({\n code,\n language,\n className,\n}) => (\n <div\n className={cn('overflow-hidden rounded-xl border border-border-seam bg-bg-sunken', className)}\n >\n <div className=\"flex items-center justify-between border-b border-border-seam bg-bg-surface px-3 py-1.5\">\n <span className=\"inline-flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-text-muted\">\n <Terminal className=\"h-3 w-3\" aria-hidden=\"true\" />\n {language}\n </span>\n <CopyButton value={code} label=\"Copy\" />\n </div>\n <div className=\"overflow-x-auto\">\n <pre className=\"min-w-0 px-4 py-3 text-[13px] leading-relaxed\">\n <code className=\"font-mono text-text-primary\">{highlightCode(code)}</code>\n </pre>\n </div>\n </div>\n);\n\n// ============================================================================\n// Small presentational helpers\n// ============================================================================\n\nconst SectionHeading: FC<{ icon: typeof Rocket; title: string; children?: ReactNode }> = ({\n icon: Icon,\n title,\n children,\n}) => (\n <div className=\"mb-4\">\n <h2 className=\"flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Icon className=\"h-5 w-5 text-action-primary-bg\" aria-hidden=\"true\" />\n {title}\n </h2>\n {children && <p className=\"mt-1 max-w-2xl text-sm text-text-secondary\">{children}</p>}\n </div>\n);\n\nconst InlineCode: FC<{ children: ReactNode }> = ({ children }) => (\n <code className=\"rounded bg-bg-sunken px-1.5 py-0.5 font-mono text-[0.85em] text-text-primary\">\n {children}\n </code>\n);\n\n// ============================================================================\n// Getting Started\n// ============================================================================\n\nconst GETTING_STARTED_STEPS: { title: string; body: ReactNode }[] = [\n {\n title: 'Create an API key',\n body: (\n <>\n Head to the <strong>API Keys</strong> tab and mint a key for your registered app. The secret\n is shown once at creation — copy it somewhere safe.\n </>\n ),\n },\n {\n title: 'Point at the GraphQL endpoint',\n body: (\n <>\n All HookMovies data is served from a single GraphQL API. The endpoint is shown\n in the <strong>GraphQL API</strong> tab.\n </>\n ),\n },\n {\n title: 'Authenticate every request',\n body: (\n <>\n Send your key as a bearer token in the <InlineCode>Authorization</InlineCode> header:{' '}\n <InlineCode>Authorization: Bearer <apiSecret></InlineCode>.\n </>\n ),\n },\n {\n title: 'Make your first query',\n body: (\n <>\n Call <InlineCode>hokListTitles</InlineCode> to page through the catalog, then drill into a\n title with <InlineCode>hokTitle(id)</InlineCode>.\n </>\n ),\n },\n];\n\nconst GettingStartedSection: FC<{ endpoint: string }> = ({ endpoint }) => (\n <div className=\"space-y-6\">\n <SectionHeading icon={Rocket} title=\"Getting Started\">\n Build with the HookMovies platform API — a single GraphQL gateway that powers the catalog,\n productions, auditions, entitlements and playback surfaces you see in this app.\n </SectionHeading>\n\n <ol className=\"grid gap-4 sm:grid-cols-2\">\n {GETTING_STARTED_STEPS.map((step, i) => (\n <li key={step.title}>\n <GlassCard treatment=\"glass\" className=\"h-full border-border-seam p-4\">\n <div className=\"flex items-start gap-3\">\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-action-primary-bg text-sm font-semibold text-action-primary-text\">\n {i + 1}\n </span>\n <div className=\"min-w-0\">\n <h3 className=\"text-sm font-semibold text-text-primary\">{step.title}</h3>\n <p className=\"mt-1 text-sm text-text-secondary\">{step.body}</p>\n </div>\n </div>\n </GlassCard>\n </li>\n ))}\n </ol>\n\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-4\">\n <div className=\"flex items-center gap-2 text-sm font-medium text-text-primary\">\n <BookOpen className=\"h-4 w-4 text-action-primary-bg\" aria-hidden=\"true\" />\n Base endpoint\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Every request targets the workspace GraphQL gateway:\n </p>\n <div className=\"mt-3 flex flex-wrap items-center gap-2\">\n <code className=\"min-w-0 flex-1 overflow-x-auto rounded-lg border border-border-seam bg-bg-sunken px-3 py-2 font-mono text-sm text-text-primary\">\n {endpoint}\n </code>\n <CopyButton value={endpoint} label=\"Copy URL\" />\n </div>\n </GlassCard>\n </div>\n);\n\n// ============================================================================\n// GraphQL API\n// ============================================================================\n\nconst SAMPLE_QUERY = `query FirstTitles {\n hokListTitles(pagination: { limit: 10, offset: 0 }, status: PUBLISHED) {\n items {\n id\n title\n type\n status\n releaseYear\n genres\n }\n pageInfo { cursor hasMore }\n totalCount\n }\n}`;\n\nconst SAMPLE_RESPONSE = `{\n \"data\": {\n \"hokListTitles\": {\n \"items\": [\n {\n \"id\": \"9f2c…\",\n \"title\": \"Midnight in Marseille\",\n \"type\": \"MOVIE\",\n \"status\": \"PUBLISHED\",\n \"releaseYear\": 2025,\n \"genres\": [\"Drama\", \"Thriller\"]\n }\n ],\n \"pageInfo\": { \"cursor\": null, \"hasMore\": true },\n \"totalCount\": 128\n }\n }\n}`;\n\nconst GraphqlSection: FC<{ endpoint: string }> = ({ endpoint }) => {\n const curl = `curl -X POST '${endpoint}' \\\\\n -H 'Content-Type: application/json' \\\\\n -H 'Authorization: Bearer <apiSecret>' \\\\\n -d '{\"query\": \"query { hokListTitles(pagination: { limit: 5 }) { items { id title } totalCount } }\"}'`;\n\n return (\n <div className=\"space-y-6\">\n <SectionHeading icon={Code2} title=\"GraphQL API\">\n One GraphQL endpoint serves the entire HookMovies domain. Send POST requests with\n your API key as a bearer token.\n </SectionHeading>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Endpoint</h3>\n <div className=\"flex flex-wrap items-center gap-2\">\n <code className=\"min-w-0 flex-1 overflow-x-auto rounded-lg border border-border-seam bg-bg-sunken px-3 py-2 font-mono text-sm text-text-primary\">\n {endpoint}\n </code>\n <CopyButton value={endpoint} label=\"Copy URL\" />\n </div>\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Sample query</h3>\n <CodeBlock language=\"graphql\" code={SAMPLE_QUERY} />\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Sample response</h3>\n <CodeBlock language=\"json\" code={SAMPLE_RESPONSE} />\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Run it with cURL</h3>\n <CodeBlock language=\"bash\" code={curl} />\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// API Keys — reuses global-auth via the shared data-layer hooks\n// ============================================================================\n\nconst SecretReveal: FC<{ result: ApiKeyResult; onDismiss: () => void }> = ({\n result,\n onDismiss,\n}) => (\n <GlassCard\n treatment=\"glass\"\n className=\"mb-6 border-status-warning-border bg-status-warning-bg-subtle p-5\"\n >\n <div className=\"flex items-start gap-3\">\n <ShieldAlert\n className=\"mt-0.5 h-5 w-5 shrink-0 text-status-warning-text\"\n aria-hidden=\"true\"\n />\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Save your API secret now</h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n This secret is shown only once and cannot be retrieved again. Store it somewhere safe\n before dismissing this panel.\n </p>\n\n <dl className=\"mt-4 space-y-3\">\n {(\n [\n { label: 'API key', value: result.apiKey },\n { label: 'API secret', value: result.apiSecret },\n { label: 'Key ID', value: result.keyId },\n ] as const\n ).map((row) => (\n <div key={row.label}>\n <dt className=\"mb-1 text-xs font-medium text-text-secondary\">{row.label}</dt>\n <dd className=\"flex flex-wrap items-center gap-2\">\n <code className=\"min-w-0 flex-1 overflow-x-auto rounded-lg border border-border-seam bg-bg-surface px-3 py-2 font-mono text-sm text-text-primary\">\n {row.value}\n </code>\n <CopyButton value={row.value} label=\"Copy\" />\n </dd>\n </div>\n ))}\n </dl>\n\n <button\n type=\"button\"\n onClick={onDismiss}\n className=\"mt-4 rounded-lg border border-border-seam bg-bg-surface px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n I've saved it — dismiss\n </button>\n </div>\n </div>\n </GlassCard>\n);\n\nconst fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelClass = 'mb-1.5 block text-xs font-medium text-text-secondary';\n\nconst GenerateKeyForm: FC<{\n onGenerated: (result: ApiKeyResult) => void;\n onClose: () => void;\n}> = ({ onGenerated, onClose }) => {\n const generate = useGenerateApiKey();\n const [name, setName] = useState('');\n const [appId, setAppId] = useState('');\n const [scopes, setScopes] = useState<string[]>(['read']);\n const [expiresAt, setExpiresAt] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const toggleScope = (value: string) => {\n setScopes((prev) =>\n prev.includes(value) ? prev.filter((s) => s !== value) : [...prev, value]\n );\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n if (!name.trim()) {\n setError('Give the key a recognisable name.');\n return;\n }\n if (!appId.trim()) {\n setError('Enter the App ID this key will act on behalf of.');\n return;\n }\n if (scopes.length === 0) {\n setError('Select at least one scope.');\n return;\n }\n try {\n const result = await generate.mutateAsync({\n appId: appId.trim(),\n name: name.trim(),\n scope: scopes,\n expiresAt: expiresAt ? new Date(expiresAt).toISOString() : undefined,\n });\n onGenerated(result);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to generate the API key.');\n }\n };\n\n return (\n <GlassCard treatment=\"glass\" className=\"mb-6 border-border-seam p-5\">\n <h3 className=\"mb-4 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <KeyRound className=\"h-4 w-4 text-action-primary-bg\" aria-hidden=\"true\" />\n Generate a new API key\n </h3>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {error && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label htmlFor=\"key-name\" className={labelClass}>\n Key name *\n </label>\n <input\n id=\"key-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Production ingest\"\n className={fieldClass}\n required\n />\n </div>\n <div>\n <label htmlFor=\"key-app-id\" className={labelClass}>\n App ID *\n </label>\n <input\n id=\"key-app-id\"\n value={appId}\n onChange={(e) => setAppId(e.target.value)}\n placeholder=\"Your registered app's ID\"\n className={fieldClass}\n required\n />\n <p className=\"mt-1 text-[11px] text-text-muted\">\n The client app this key acts on behalf of — find it in your workspace app settings.\n </p>\n </div>\n </div>\n\n <fieldset>\n <legend className={labelClass}>Scopes *</legend>\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-2\">\n {SCOPE_OPTIONS.map((scope) => {\n const checked = scopes.includes(scope.value);\n return (\n <label\n key={scope.value}\n className={cn(\n 'flex cursor-pointer items-start gap-2 rounded-lg border p-3 text-sm transition-colors',\n checked\n ? 'border-action-primary-bg bg-action-primary-bg/10'\n : 'border-border-subtle hover:border-border-strong'\n )}\n >\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={() => toggleScope(scope.value)}\n className=\"mt-0.5 accent-[var(--color-action-primary-bg)]\"\n />\n <span className=\"min-w-0\">\n <span className=\"block font-medium text-text-primary\">{scope.label}</span>\n <span className=\"block text-xs text-text-secondary\">{scope.description}</span>\n </span>\n </label>\n );\n })}\n </div>\n </fieldset>\n\n <div>\n <label htmlFor=\"key-expires\" className={labelClass}>\n Expires (optional)\n </label>\n <input\n id=\"key-expires\"\n type=\"date\"\n value={expiresAt}\n onChange={(e) => setExpiresAt(e.target.value)}\n className={cn(fieldClass, 'sm:max-w-xs')}\n />\n </div>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"submit\"\n disabled={generate.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90 disabled:opacity-50\"\n >\n {generate.isPending ? 'Generating…' : 'Generate key'}\n </button>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n Cancel\n </button>\n </div>\n </form>\n </GlassCard>\n );\n};\n\nconst ApiKeyRow: FC<{ apiKey: ApiKey }> = ({ apiKey }) => {\n const revoke = useRevokeApiKey();\n const [confirming, setConfirming] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleRevoke = async () => {\n setError(null);\n try {\n await revoke.mutateAsync(apiKey.id);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to revoke this key.');\n setConfirming(false);\n }\n };\n\n return (\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-semibold text-text-primary\">{apiKey.name}</span>\n <code className=\"rounded bg-bg-sunken px-1.5 py-0.5 font-mono text-xs text-text-secondary\">\n {apiKey.prefix}…\n </code>\n </div>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-muted\">\n {apiKey.appName && <span>{apiKey.appName}</span>}\n <span aria-hidden=\"true\">·</span>\n <span>\n Created {formatDate(apiKey.createdAt, { hour: undefined, minute: undefined })}\n </span>\n <span aria-hidden=\"true\">·</span>\n <span>\n {apiKey.lastUsedAt\n ? `Last used ${formatRelativeTime(apiKey.lastUsedAt)}`\n : 'Never used'}\n </span>\n {apiKey.expiresAt && (\n <>\n <span aria-hidden=\"true\">·</span>\n <span>\n Expires {formatDate(apiKey.expiresAt, { hour: undefined, minute: undefined })}\n </span>\n </>\n )}\n </div>\n {apiKey.scopes.length > 0 && (\n <div className=\"mt-2 flex flex-wrap gap-1.5\">\n {apiKey.scopes.map((scope) => (\n <span\n key={scope}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-text-secondary\"\n >\n {scope}\n </span>\n ))}\n </div>\n )}\n </div>\n\n <div className=\"shrink-0\">\n {confirming ? (\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n disabled={revoke.isPending}\n onClick={() => void handleRevoke()}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-1.5 text-xs font-medium text-status-error-text transition-opacity hover:opacity-90 disabled:opacity-50\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {revoke.isPending ? 'Revoking…' : 'Confirm revoke'}\n </button>\n <button\n type=\"button\"\n onClick={() => setConfirming(false)}\n className=\"rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n Cancel\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={() => setConfirming(true)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:border-status-error-border hover:text-status-error-text\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n Revoke\n </button>\n )}\n </div>\n </div>\n {error && <p className=\"mt-2 text-xs text-status-error-text\">{error}</p>}\n </GlassCard>\n );\n};\n\nconst ApiKeysSection: FC = () => {\n const { data, isLoading, isError, refetch } = useMyApiKeys();\n const [showForm, setShowForm] = useState(false);\n const [revealed, setRevealed] = useState<ApiKeyResult | null>(null);\n\n const keys = data?.apiKeys ?? [];\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <SectionHeading icon={KeyRound} title=\"API Keys\">\n Keys authenticate server-to-server calls to the HookMovies GraphQL gateway. They are\n issued and revoked through the platform identity service — HookMovies never stores your\n secret.\n </SectionHeading>\n <button\n type=\"button\"\n onClick={() => setShowForm((v) => !v)}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n New key\n </button>\n </div>\n\n {revealed && <SecretReveal result={revealed} onDismiss={() => setRevealed(null)} />}\n\n {showForm && (\n <GenerateKeyForm\n onGenerated={(result) => {\n setRevealed(result);\n setShowForm(false);\n }}\n onClose={() => setShowForm(false)}\n />\n )}\n\n {isLoading ? (\n <LoadingState label=\"Loading your API keys…\" />\n ) : isError ? (\n <ErrorState message=\"We couldn't load your API keys.\" onRetry={() => void refetch()} />\n ) : keys.length === 0 ? (\n <EmptyState\n icon={<KeyRound className=\"h-12 w-12\" />}\n title=\"No API keys yet\"\n description=\"Generate your first key to start calling the HookMovies API.\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowForm(true)}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-opacity hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" aria-hidden=\"true\" />\n Generate key\n </button>\n }\n />\n ) : (\n <div className=\"space-y-3\">\n {keys.map((apiKey) => (\n <ApiKeyRow key={apiKey.id} apiKey={apiKey} />\n ))}\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// SDKs & MCP\n// ============================================================================\n\ninterface PackageCard {\n name: string;\n repo: string;\n description: string;\n icon: typeof Package;\n install: { language: string; code: string };\n}\n\nconst PACKAGES: PackageCard[] = [\n {\n name: 'Node SDK',\n repo: 'hookmovies-sdk-node',\n description:\n 'Typed TypeScript client for the HookMovies GraphQL API — catalog, titles, reviews and playback.',\n icon: Boxes,\n install: {\n language: 'bash',\n code: 'bun add hookmovies-sdk-node\\n# or: npm install hookmovies-sdk-node',\n },\n },\n {\n name: 'Python SDK',\n repo: 'hookmovies-sdk-python',\n description: 'Pythonic client with async support for backend integrations and data pipelines.',\n icon: Package,\n install: { language: 'bash', code: 'pip install hookmovies-sdk-python' },\n },\n {\n name: 'MCP Server',\n repo: 'hookmovies-mcp',\n description:\n 'Model Context Protocol server that exposes HookMovies tools to AI agents and assistants.',\n icon: Boxes,\n install: { language: 'bash', code: 'bunx hookmovies-mcp --help' },\n },\n {\n name: 'CLI',\n repo: 'hookmovies-cli',\n description:\n 'Command-line tool to manage titles, productions and rights from your terminal or CI.',\n icon: Terminal,\n install: { language: 'bash', code: 'bunx hookmovies-cli titles list' },\n },\n];\n\nconst SdksSection: FC = () => (\n <div className=\"space-y-6\">\n <SectionHeading icon={Package} title=\"SDKs & MCP\">\n Official HookMovies packages wrap the GraphQL API so you can build in your language of choice\n — or plug the platform straight into an AI agent via MCP.\n </SectionHeading>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n {PACKAGES.map((pkg) => {\n const Icon = pkg.icon;\n const repoUrl = `https://github.com/algoshred/${pkg.repo}`;\n return (\n <GlassCard\n key={pkg.repo}\n treatment=\"glass\"\n className=\"flex h-full flex-col border-border-seam p-4\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-lg bg-action-primary-bg/10\">\n <Icon className=\"h-5 w-5 text-action-primary-bg\" aria-hidden=\"true\" />\n </span>\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary\">{pkg.name}</h3>\n <code className=\"text-[11px] text-text-muted\">{pkg.repo}</code>\n </div>\n </div>\n <a\n href={repoUrl}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"inline-flex items-center gap-1 rounded-md border border-border-seam px-2 py-1 text-xs font-medium text-text-secondary transition-colors hover:border-border-strong hover:text-text-primary\"\n >\n Repo\n <ExternalLink className=\"h-3 w-3\" aria-hidden=\"true\" />\n </a>\n </div>\n <p className=\"mt-3 flex-1 text-sm text-text-secondary\">{pkg.description}</p>\n <div className=\"mt-3\">\n <CodeBlock language={pkg.install.language} code={pkg.install.code} />\n </div>\n </GlassCard>\n );\n })}\n </div>\n </div>\n);\n\n// ============================================================================\n// Embeddable Player\n// ============================================================================\n\nconst EmbedSection: FC<{ watchUrl: string }> = ({ watchUrl }) => {\n const iframeSnippet = `<iframe\n src=\"${watchUrl}\"\n width=\"100%\"\n height=\"480\"\n frameborder=\"0\"\n allow=\"autoplay; fullscreen; encrypted-media\"\n allowfullscreen\n title=\"HookMovies player\"\n></iframe>`;\n\n const responsiveSnippet = `<div style=\"position: relative; padding-top: 56.25%;\">\n <iframe\n src=\"${watchUrl}\"\n style=\"position: absolute; inset: 0; width: 100%; height: 100%; border: 0;\"\n allow=\"autoplay; fullscreen; encrypted-media\"\n allowfullscreen\n title=\"HookMovies player\"\n ></iframe>\n</div>`;\n\n return (\n <div className=\"space-y-6\">\n <SectionHeading icon={Clapperboard} title=\"Embeddable Player\">\n Drop a HookMovies title into any web page with a single{' '}\n <InlineCode><iframe></InlineCode>. Replace <InlineCode>YOUR_TITLE_ID</InlineCode> with\n the id of any published title.\n </SectionHeading>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Basic embed</h3>\n <CodeBlock language=\"html\" code={iframeSnippet} />\n </div>\n\n <div>\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">Responsive (16:9) embed</h3>\n <p className=\"mb-2 text-sm text-text-secondary\">\n Wrap the iframe in an aspect-ratio box so the player scales fluidly on any device.\n </p>\n <CodeBlock language=\"html\" code={responsiveSnippet} />\n </div>\n\n <GlassCard treatment=\"metric\" className=\"border-border-seam p-4\">\n <p className=\"text-sm text-text-secondary\">\n The embedded player respects the viewer's entitlements and profile preferences. Viewers\n who aren't entitled to a title are prompted to sign in or purchase access inside the\n frame.\n </p>\n </GlassCard>\n </div>\n );\n};\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport const DevPortalPage: FC = () => {\n const { apiGatewayUrl, getPath } = useHookMovies();\n const [active, setActive] = useState<SectionId>('overview');\n\n const endpoint = apiGatewayUrl || 'https://graphqlworkspaces.burdenoff.com/workspaces/graphql';\n\n const watchUrl = useMemo(() => {\n const origin =\n typeof window !== 'undefined' ? window.location.origin : 'https://app.hookmovies.com';\n return `${origin}${getPath('watch/YOUR_TITLE_ID')}`;\n }, [getPath]);\n\n return (\n <PageLayout\n title=\"Developer Portal\"\n description=\"Build on the HookMovies platform API\"\n purpose=\"Everything you need to integrate with HookMovies — authenticate with an API key, query the GraphQL gateway, pull in the official SDKs or MCP server, and embed the player anywhere. API keys are issued through the platform identity service; HookMovies never stores your secret.\"\n >\n {/* Section navigation — horizontal, scrollable on small screens */}\n <div\n className=\"mb-6 flex gap-1 overflow-x-auto rounded-xl border border-border-seam bg-bg-surface p-1\"\n role=\"tablist\"\n aria-label=\"Developer portal sections\"\n >\n {SECTIONS.map((section) => {\n const Icon = section.icon;\n const selected = active === section.id;\n return (\n <button\n key={section.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={selected}\n onClick={() => setActive(section.id)}\n className={cn(\n 'inline-flex shrink-0 items-center gap-2 rounded-lg px-3.5 py-2 text-sm font-medium transition-colors',\n selected\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:text-text-primary'\n )}\n >\n <Icon className=\"h-4 w-4\" aria-hidden=\"true\" />\n {section.label}\n </button>\n );\n })}\n </div>\n\n <div className=\"max-w-4xl\">\n {active === 'overview' && <GettingStartedSection endpoint={endpoint} />}\n {active === 'graphql' && <GraphqlSection endpoint={endpoint} />}\n {active === 'keys' && <ApiKeysSection />}\n {active === 'sdks' && <SdksSection />}\n {active === 'embed' && <EmbedSection watchUrl={watchUrl} />}\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-hookmovies",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.719.1",
|
|
4
4
|
"description": "Burdenoff HookMovies Microfrontend — discovery, catalog, engagement, auditions, productions, distribution, and library surfaces for the global creative-media network.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|