@sequenceholdings/artifact-studio 0.2.6 → 0.3.0
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/VERSION-PIN.md +8 -1
- package/dist/api.d.ts +11 -1
- package/dist/api.js +26 -9
- package/dist/build.d.ts +2 -2
- package/dist/build.js +4 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +105 -10
- package/dist/dev-link.d.ts +1 -1
- package/dist/dev-link.js +2 -1
- package/dist/gateway/admission.d.ts +55 -0
- package/dist/gateway/admission.js +132 -0
- package/dist/gateway/control.d.ts +91 -0
- package/dist/gateway/control.js +113 -0
- package/dist/gateway/hosted-proxy.d.ts +36 -0
- package/dist/gateway/hosted-proxy.js +347 -0
- package/dist/gateway/release-admission.d.ts +23 -0
- package/dist/gateway/release-admission.js +259 -0
- package/dist/gateway/server.d.ts +29 -0
- package/dist/gateway/server.js +298 -0
- package/dist/gateway/session.d.ts +71 -0
- package/dist/gateway/session.js +143 -0
- package/dist/gateway/usage.d.ts +37 -0
- package/dist/gateway/usage.js +46 -0
- package/dist/gateway-main.d.ts +1 -0
- package/dist/gateway-main.js +84 -0
- package/dist/git-clone.d.ts +23 -1
- package/dist/git-clone.js +32 -15
- package/dist/manifest-next.d.ts +217 -0
- package/dist/manifest-next.js +120 -0
- package/dist/manifest-static.d.ts +76 -0
- package/dist/manifest-static.js +124 -0
- package/dist/manifest.d.ts +118 -13
- package/dist/manifest.js +12 -119
- package/dist/next/bridge.d.ts +47 -0
- package/dist/next/bridge.js +229 -0
- package/dist/next/browser-logs.d.ts +7 -0
- package/dist/next/browser-logs.js +51 -0
- package/dist/next/browser.d.ts +6 -0
- package/dist/next/browser.js +57 -0
- package/dist/next/protocol.d.ts +73 -0
- package/dist/next/protocol.js +56 -0
- package/dist/next/release.d.ts +15 -0
- package/dist/next/release.js +46 -0
- package/dist/next/requests.d.ts +15 -0
- package/dist/next/requests.js +59 -0
- package/dist/next/routes.d.ts +4 -0
- package/dist/next/routes.js +21 -0
- package/dist/next/streams.d.ts +16 -0
- package/dist/next/streams.js +132 -0
- package/dist/next/theme.d.ts +10 -0
- package/dist/next/theme.js +45 -0
- package/dist/next/transport.d.ts +15 -0
- package/dist/next/transport.js +13 -0
- package/dist/next-build.d.ts +29 -0
- package/dist/next-build.js +129 -0
- package/dist/next-cache/adapter-runtime.d.ts +51 -0
- package/dist/next-cache/adapter-runtime.js +73 -0
- package/dist/next-cache/component-handler.d.ts +3 -0
- package/dist/next-cache/component-handler.js +10 -0
- package/dist/next-cache/components.d.ts +20 -0
- package/dist/next-cache/components.js +113 -0
- package/dist/next-cache/config.d.ts +9 -0
- package/dist/next-cache/config.js +78 -0
- package/dist/next-cache/incremental-handler.d.ts +18 -0
- package/dist/next-cache/incremental-handler.js +17 -0
- package/dist/next-cache/incremental.d.ts +27 -0
- package/dist/next-cache/incremental.js +99 -0
- package/dist/next-cache/index.d.ts +18 -0
- package/dist/next-cache/index.js +16 -0
- package/dist/next-cache/memory-store.d.ts +27 -0
- package/dist/next-cache/memory-store.js +118 -0
- package/dist/next-cache/redis-scripts.d.ts +3 -0
- package/dist/next-cache/redis-scripts.js +85 -0
- package/dist/next-cache/redis-store.d.ts +18 -0
- package/dist/next-cache/redis-store.js +56 -0
- package/dist/next-cache/runtime.d.ts +29 -0
- package/dist/next-cache/runtime.js +63 -0
- package/dist/next-cache/serialization.d.ts +39 -0
- package/dist/next-cache/serialization.js +333 -0
- package/dist/next-cache/store.d.ts +54 -0
- package/dist/next-cache/store.js +45 -0
- package/dist/next-cache/valkey-transport.d.ts +52 -0
- package/dist/next-cache/valkey-transport.js +189 -0
- package/dist/next-dev-control.d.ts +48 -0
- package/dist/next-dev-control.js +63 -0
- package/dist/next-dev-process.d.ts +17 -0
- package/dist/next-dev-process.js +110 -0
- package/dist/next-dev.d.ts +12 -0
- package/dist/next-dev.js +267 -0
- package/dist/next-image-config.d.ts +9 -0
- package/dist/next-image-config.js +101 -0
- package/dist/next-project.d.ts +22 -0
- package/dist/next-project.js +64 -0
- package/dist/next-public-fonts-local.d.ts +7 -0
- package/dist/next-public-fonts-local.js +64 -0
- package/dist/next-public-fonts.d.ts +13 -0
- package/dist/next-public-fonts.js +49 -0
- package/dist/next-source-hash.d.ts +14 -0
- package/dist/next-source-hash.js +18 -0
- package/dist/next-source.d.ts +35 -0
- package/dist/next-source.js +155 -0
- package/dist/next.d.ts +9 -0
- package/dist/next.js +5 -0
- package/dist/project.d.ts +3 -0
- package/dist/project.js +9 -1
- package/dist/runtime-contracts.d.ts +229 -0
- package/dist/runtime-contracts.js +224 -0
- package/dist/sdk.d.ts +2 -2
- package/dist/server/broker-contract.d.ts +68 -0
- package/dist/server/broker-contract.js +33 -0
- package/dist/server/client.d.ts +67 -0
- package/dist/server/client.js +159 -0
- package/dist/server/logger.d.ts +26 -0
- package/dist/server/logger.js +24 -0
- package/dist/server/release-forms.d.ts +13 -0
- package/dist/server/release-forms.js +124 -0
- package/dist/server/request-context.d.ts +74 -0
- package/dist/server/request-context.js +103 -0
- package/dist/server/runtime-config.d.ts +53 -0
- package/dist/server/runtime-config.js +64 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.js +4 -0
- package/dist/templates/nextjs/.artifactignore +14 -0
- package/dist/templates/nextjs/README.md +151 -0
- package/dist/templates/nextjs/app/about/page.tsx +26 -0
- package/dist/templates/nextjs/app/actions.ts +18 -0
- package/dist/templates/nextjs/app/api/health/route.ts +6 -0
- package/dist/templates/nextjs/app/components/counter.tsx +19 -0
- package/dist/templates/nextjs/app/components/greeting-form.tsx +38 -0
- package/dist/templates/nextjs/app/globals.css +4 -0
- package/dist/templates/nextjs/app/layout.tsx +27 -0
- package/dist/templates/nextjs/app/page.tsx +51 -0
- package/dist/templates/nextjs/app/providers.tsx +107 -0
- package/dist/templates/nextjs/artifact.bundle.yml +55 -0
- package/dist/templates/nextjs/next.config.mjs +27 -0
- package/dist/templates/nextjs/package.json +32 -0
- package/dist/templates/nextjs/pnpm-workspace.yaml +8 -0
- package/dist/templates/nextjs/postcss.config.mjs +5 -0
- package/dist/templates/nextjs/tsconfig.json +20 -0
- package/dist/templates/react-vite/package.json +1 -1
- package/dist/tunnel/broker.d.ts +75 -0
- package/dist/tunnel/broker.js +451 -0
- package/dist/tunnel/client.d.ts +22 -0
- package/dist/tunnel/client.js +177 -0
- package/dist/tunnel/index.d.ts +6 -0
- package/dist/tunnel/index.js +4 -0
- package/dist/tunnel/io.d.ts +33 -0
- package/dist/tunnel/io.js +108 -0
- package/dist/tunnel/peer.d.ts +25 -0
- package/dist/tunnel/peer.js +147 -0
- package/dist/tunnel/protocol.d.ts +69 -0
- package/dist/tunnel/protocol.js +196 -0
- package/dist/tunnel/redis-ownership.d.ts +38 -0
- package/dist/tunnel/redis-ownership.js +100 -0
- package/dist/tunnel/redis-socket.d.ts +13 -0
- package/dist/tunnel/redis-socket.js +48 -0
- package/dist/tunnel/redis-types.d.ts +70 -0
- package/dist/tunnel/redis-types.js +9 -0
- package/dist/tunnel/redis.d.ts +53 -0
- package/dist/tunnel/redis.js +391 -0
- package/dist/tunnel/transport.d.ts +17 -0
- package/dist/tunnel/transport.js +1 -0
- package/package.json +67 -4
- package/templates/nextjs/.artifactignore +14 -0
- package/templates/nextjs/README.md +151 -0
- package/templates/nextjs/app/about/page.tsx +26 -0
- package/templates/nextjs/app/actions.ts +18 -0
- package/templates/nextjs/app/api/health/route.ts +6 -0
- package/templates/nextjs/app/components/counter.tsx +19 -0
- package/templates/nextjs/app/components/greeting-form.tsx +38 -0
- package/templates/nextjs/app/globals.css +4 -0
- package/templates/nextjs/app/layout.tsx +27 -0
- package/templates/nextjs/app/page.tsx +51 -0
- package/templates/nextjs/app/providers.tsx +107 -0
- package/templates/nextjs/artifact.bundle.yml +55 -0
- package/templates/nextjs/next.config.mjs +27 -0
- package/templates/nextjs/package.json +32 -0
- package/templates/nextjs/pnpm-workspace.yaml +8 -0
- package/templates/nextjs/postcss.config.mjs +5 -0
- package/templates/nextjs/tsconfig.json +20 -0
- package/templates/react-vite/package.json +1 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { Button } from '@sequenceholdings/atlas-ui/next'
|
|
5
|
+
|
|
6
|
+
export function Counter() {
|
|
7
|
+
const [count, setCount] = useState(0)
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div className="flex items-center gap-stack-group">
|
|
11
|
+
<Button type="button" onClick={() => setCount(value => value + 1)}>
|
|
12
|
+
Increment
|
|
13
|
+
</Button>
|
|
14
|
+
<output className="font-mono text-lg tabular-nums" aria-live="polite">
|
|
15
|
+
{count}
|
|
16
|
+
</output>
|
|
17
|
+
</div>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useActionState } from 'react'
|
|
4
|
+
import { Button, Input } from '@sequenceholdings/atlas-ui/next'
|
|
5
|
+
import { greet, type GreetingState } from '../actions'
|
|
6
|
+
|
|
7
|
+
const initialState: GreetingState = { message: '', error: null }
|
|
8
|
+
|
|
9
|
+
export function GreetingForm() {
|
|
10
|
+
const [state, action, pending] = useActionState(greet, initialState)
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<form action={action} className="space-y-4">
|
|
14
|
+
<div className="space-y-2">
|
|
15
|
+
<label htmlFor="name" className="text-sm font-medium">Your name</label>
|
|
16
|
+
<Input
|
|
17
|
+
id="name"
|
|
18
|
+
name="name"
|
|
19
|
+
autoComplete="given-name"
|
|
20
|
+
required
|
|
21
|
+
maxLength={80}
|
|
22
|
+
aria-invalid={Boolean(state.error)}
|
|
23
|
+
aria-describedby="greeting-result"
|
|
24
|
+
/>
|
|
25
|
+
</div>
|
|
26
|
+
<Button type="submit" disabled={pending}>
|
|
27
|
+
{pending ? 'Sending…' : 'Say hello'}
|
|
28
|
+
</Button>
|
|
29
|
+
<p
|
|
30
|
+
id="greeting-result"
|
|
31
|
+
aria-live="polite"
|
|
32
|
+
className={state.error ? 'text-sm text-destructive' : 'text-sm text-foreground-secondary'}
|
|
33
|
+
>
|
|
34
|
+
{state.error ?? state.message}
|
|
35
|
+
</p>
|
|
36
|
+
</form>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Metadata } from 'next'
|
|
2
|
+
import { headers } from 'next/headers'
|
|
3
|
+
import type { ReactNode } from 'react'
|
|
4
|
+
import {
|
|
5
|
+
getArtifactBrowserBootstrap,
|
|
6
|
+
getArtifactRequestContext,
|
|
7
|
+
} from '@sequenceholdings/artifact-studio/server'
|
|
8
|
+
import { Providers } from './providers'
|
|
9
|
+
import './globals.css'
|
|
10
|
+
|
|
11
|
+
export const metadata: Metadata = {
|
|
12
|
+
title: '{{title}}',
|
|
13
|
+
description: 'A full-stack application managed by Atlas',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default async function RootLayout({ children }: { children: ReactNode }) {
|
|
17
|
+
const context = await getArtifactRequestContext({ headers: await headers() })
|
|
18
|
+
const bootstrap = context ? getArtifactBrowserBootstrap(context) : null
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<html lang="en" className={bootstrap?.initialContext.theme?.dark ? 'dark' : undefined}>
|
|
22
|
+
<body className="min-h-screen bg-background font-sans text-foreground antialiased">
|
|
23
|
+
<Providers bootstrap={bootstrap}>{children}</Providers>
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import Link from 'next/link'
|
|
2
|
+
import { connection } from 'next/server'
|
|
3
|
+
import { Counter } from './components/counter'
|
|
4
|
+
import { GreetingForm } from './components/greeting-form'
|
|
5
|
+
|
|
6
|
+
export default async function HomePage() {
|
|
7
|
+
await connection()
|
|
8
|
+
const renderedAt = new Date().toISOString()
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<main className="mx-auto max-w-5xl px-roomy py-10 sm:px-8">
|
|
12
|
+
<header className="mb-10 border-b border-border pb-8">
|
|
13
|
+
<p className="mb-3 text-xs font-semibold uppercase tracking-widest text-primary">
|
|
14
|
+
Artifact Studio / Next.js
|
|
15
|
+
</p>
|
|
16
|
+
<h1 className="text-3xl font-semibold tracking-tight">{{title}}</h1>
|
|
17
|
+
<p className="mt-3 max-w-2xl text-foreground-secondary">
|
|
18
|
+
Build your interface and server together. Develop on your machine,
|
|
19
|
+
preview in Atlas and publish an application that runs independently.
|
|
20
|
+
</p>
|
|
21
|
+
</header>
|
|
22
|
+
|
|
23
|
+
<div className="grid gap-stack-section md:grid-cols-2">
|
|
24
|
+
<section className="rounded-lg border border-border bg-card p-6" aria-labelledby="client-heading">
|
|
25
|
+
<h2 id="client-heading" className="text-lg font-semibold">Client interaction</h2>
|
|
26
|
+
<p className="mb-6 mt-2 text-sm text-muted-foreground">
|
|
27
|
+
Increment the counter, then edit its label. Fast Refresh preserves
|
|
28
|
+
client state for compatible edits.
|
|
29
|
+
</p>
|
|
30
|
+
<Counter />
|
|
31
|
+
</section>
|
|
32
|
+
|
|
33
|
+
<section className="rounded-lg border border-border bg-card p-6" aria-labelledby="server-heading">
|
|
34
|
+
<h2 id="server-heading" className="text-lg font-semibold">Server action</h2>
|
|
35
|
+
<p className="mb-6 mt-2 text-sm text-muted-foreground">
|
|
36
|
+
Submit a name to a validated server action. This example does not
|
|
37
|
+
store data or require a connected resource.
|
|
38
|
+
</p>
|
|
39
|
+
<GreetingForm />
|
|
40
|
+
</section>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<footer className="mt-8 flex flex-wrap items-center justify-between gap-stack-group text-sm text-muted-foreground">
|
|
44
|
+
<p>Rendered on the server: <time dateTime={renderedAt}>{renderedAt}</time></p>
|
|
45
|
+
<Link href="/about" className="font-medium text-primary underline-offset-4 hover:underline">
|
|
46
|
+
How this application works
|
|
47
|
+
</Link>
|
|
48
|
+
</footer>
|
|
49
|
+
</main>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Suspense, useEffect, useRef, useState, type ReactNode } from 'react'
|
|
4
|
+
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
|
|
5
|
+
import { PortalContainerProvider } from '@sequenceholdings/atlas-ui/next'
|
|
6
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
|
7
|
+
import {
|
|
8
|
+
createNextArtifactBridge,
|
|
9
|
+
createNextArtifactThemeApplier,
|
|
10
|
+
installNextArtifactBrowserIntegration,
|
|
11
|
+
type NextArtifactBridge,
|
|
12
|
+
type NextArtifactBridgeBootstrap,
|
|
13
|
+
type NextArtifactInitialContext,
|
|
14
|
+
} from '@sequenceholdings/artifact-studio/next'
|
|
15
|
+
|
|
16
|
+
type BrowserBootstrap = NextArtifactBridgeBootstrap & {
|
|
17
|
+
initialContext: NextArtifactInitialContext
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function Providers({ children, bootstrap }: {
|
|
21
|
+
children: ReactNode
|
|
22
|
+
bootstrap: BrowserBootstrap | null
|
|
23
|
+
}) {
|
|
24
|
+
const [queryClient] = useState(() => new QueryClient())
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<QueryClientProvider client={queryClient}>
|
|
28
|
+
<PortalContainerProvider>
|
|
29
|
+
<Suspense fallback={null}>
|
|
30
|
+
<AtlasConnection bootstrap={bootstrap} />
|
|
31
|
+
</Suspense>
|
|
32
|
+
{children}
|
|
33
|
+
</PortalContainerProvider>
|
|
34
|
+
</QueryClientProvider>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function AtlasConnection({ bootstrap }: { bootstrap: BrowserBootstrap | null }) {
|
|
39
|
+
const router = useRouter()
|
|
40
|
+
const pathname = usePathname()
|
|
41
|
+
const searchParams = useSearchParams()
|
|
42
|
+
const currentBridge = useRef<NextArtifactBridge | null>(null)
|
|
43
|
+
const [connectionError, setConnectionError] = useState(false)
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!bootstrap) return
|
|
47
|
+
let active = true
|
|
48
|
+
const theme = createNextArtifactThemeApplier({ root: document.documentElement })
|
|
49
|
+
const bridge = createNextArtifactBridge({
|
|
50
|
+
bootstrap,
|
|
51
|
+
initialContext: bootstrap.initialContext,
|
|
52
|
+
onNavigate: ({ path, replace }) => replace ? router.replace(path) : router.push(path),
|
|
53
|
+
})
|
|
54
|
+
currentBridge.current = bridge
|
|
55
|
+
const removeBrowserIntegration = installNextArtifactBrowserIntegration({ bridge })
|
|
56
|
+
const applyTheme = () => {
|
|
57
|
+
const snapshot = bridge.getSnapshot()
|
|
58
|
+
if (snapshot.theme) theme.apply({ theme: snapshot.theme })
|
|
59
|
+
}
|
|
60
|
+
const updateRoute = () => bridge.routeChanged({
|
|
61
|
+
path: `${window.location.pathname}${window.location.search}${window.location.hash}`,
|
|
62
|
+
})
|
|
63
|
+
const updateTitle = () => {
|
|
64
|
+
if (bridge.getSnapshot().status === 'ready') {
|
|
65
|
+
bridge.setDocumentTitle({ label: document.title })
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
applyTheme()
|
|
69
|
+
const unsubscribe = bridge.subscribe(applyTheme)
|
|
70
|
+
const titleObserver = new MutationObserver(updateTitle)
|
|
71
|
+
titleObserver.observe(document.head, { childList: true, characterData: true, subtree: true })
|
|
72
|
+
window.addEventListener('hashchange', updateRoute)
|
|
73
|
+
updateRoute()
|
|
74
|
+
void bridge.connect().then(() => {
|
|
75
|
+
if (!active) return
|
|
76
|
+
setConnectionError(false)
|
|
77
|
+
updateTitle()
|
|
78
|
+
}).catch(() => {
|
|
79
|
+
if (active) setConnectionError(true)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return () => {
|
|
83
|
+
active = false
|
|
84
|
+
window.removeEventListener('hashchange', updateRoute)
|
|
85
|
+
titleObserver.disconnect()
|
|
86
|
+
unsubscribe()
|
|
87
|
+
removeBrowserIntegration()
|
|
88
|
+
bridge.dispose()
|
|
89
|
+
theme.dispose()
|
|
90
|
+
currentBridge.current = null
|
|
91
|
+
}
|
|
92
|
+
}, [bootstrap, router])
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
const query = searchParams.toString()
|
|
96
|
+
currentBridge.current?.routeChanged({
|
|
97
|
+
path: `${pathname}${query ? `?${query}` : ''}${window.location.hash}`,
|
|
98
|
+
})
|
|
99
|
+
}, [pathname, searchParams])
|
|
100
|
+
|
|
101
|
+
if (!connectionError) return null
|
|
102
|
+
return (
|
|
103
|
+
<p role="alert" className="border-b border-border bg-card px-roomy py-default text-sm text-destructive">
|
|
104
|
+
The Atlas connection was interrupted. Reopen this preview from Artifact Studio.
|
|
105
|
+
</p>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
bundle:
|
|
2
|
+
name: {{slug}}
|
|
3
|
+
schema_version: 2
|
|
4
|
+
|
|
5
|
+
artifact:
|
|
6
|
+
project_id: {{slug}}
|
|
7
|
+
title: "{{title}}"
|
|
8
|
+
description: "A full-stack Next.js application managed by Atlas"
|
|
9
|
+
type: nextjs
|
|
10
|
+
app_root: .
|
|
11
|
+
|
|
12
|
+
runtime:
|
|
13
|
+
sdk: sequence
|
|
14
|
+
react: 19
|
|
15
|
+
node: 24
|
|
16
|
+
next: '16.3'
|
|
17
|
+
|
|
18
|
+
# Browser permissions and server permissions are separate declarations.
|
|
19
|
+
capabilities:
|
|
20
|
+
data:
|
|
21
|
+
read: []
|
|
22
|
+
write: []
|
|
23
|
+
api:
|
|
24
|
+
read: []
|
|
25
|
+
write: []
|
|
26
|
+
functions:
|
|
27
|
+
invoke: []
|
|
28
|
+
|
|
29
|
+
server:
|
|
30
|
+
capabilities:
|
|
31
|
+
data:
|
|
32
|
+
read: []
|
|
33
|
+
write: []
|
|
34
|
+
api:
|
|
35
|
+
read: []
|
|
36
|
+
write: []
|
|
37
|
+
functions:
|
|
38
|
+
invoke: []
|
|
39
|
+
secrets: []
|
|
40
|
+
egress: []
|
|
41
|
+
resources:
|
|
42
|
+
cpu: 1
|
|
43
|
+
memory_mb: 1024
|
|
44
|
+
concurrency: 20
|
|
45
|
+
min_instances: 0
|
|
46
|
+
max_instances: 3
|
|
47
|
+
timeout_seconds: 60
|
|
48
|
+
|
|
49
|
+
build:
|
|
50
|
+
public_env: {}
|
|
51
|
+
|
|
52
|
+
# Add a registered Atlas environment with `seq-studio artifact link -e <env>`.
|
|
53
|
+
targets:
|
|
54
|
+
local:
|
|
55
|
+
url: http://localhost:5001
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { withArtifactNextConfig } from '@sequenceholdings/artifact-studio/next-cache/config'
|
|
2
|
+
|
|
3
|
+
const devOrigin = process.env.ARTIFACT_STUDIO_DEV_ORIGIN
|
|
4
|
+
|
|
5
|
+
function allowedDevOrigins() {
|
|
6
|
+
if (!devOrigin) return []
|
|
7
|
+
const url = new URL(devOrigin)
|
|
8
|
+
if (
|
|
9
|
+
!['http:', 'https:'].includes(url.protocol) ||
|
|
10
|
+
url.username || url.password || url.pathname !== '/' || url.search || url.hash ||
|
|
11
|
+
url.hostname.includes('*')
|
|
12
|
+
) {
|
|
13
|
+
throw new Error('ARTIFACT_STUDIO_DEV_ORIGIN must be an exact HTTP(S) origin.')
|
|
14
|
+
}
|
|
15
|
+
// Next matches hostnames; the Atlas gateway validates the complete origin.
|
|
16
|
+
return [url.hostname]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** @type {import('next').NextConfig} */
|
|
20
|
+
const config = {
|
|
21
|
+
output: 'standalone',
|
|
22
|
+
poweredByHeader: false,
|
|
23
|
+
reactStrictMode: true,
|
|
24
|
+
allowedDevOrigins: allowedDevOrigins(),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default withArtifactNextConfig(config)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{slug}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"packageManager": "pnpm@10.28.2",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=24 <25"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "next dev --webpack --hostname 127.0.0.1",
|
|
11
|
+
"build": "next build",
|
|
12
|
+
"start": "next start --hostname 127.0.0.1",
|
|
13
|
+
"type-check": "next typegen && tsc --noEmit"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@sequenceholdings/artifact-studio": "{{artifactStudioVersion}}",
|
|
17
|
+
"@sequenceholdings/atlas-ui": "3.4.0",
|
|
18
|
+
"@tanstack/react-query": "^4.36.1",
|
|
19
|
+
"next": "16.3.4",
|
|
20
|
+
"react": "19.2.6",
|
|
21
|
+
"react-dom": "19.2.6",
|
|
22
|
+
"server-only": "0.0.1"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@tailwindcss/postcss": "^4.2.2",
|
|
26
|
+
"@types/node": "^24.0.0",
|
|
27
|
+
"@types/react": "^19.2.10",
|
|
28
|
+
"@types/react-dom": "^19.2.3",
|
|
29
|
+
"tailwindcss": "^4.2.2",
|
|
30
|
+
"typescript": "^5.6.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "react-jsx",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [{ "name": "next" }]
|
|
17
|
+
},
|
|
18
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"],
|
|
19
|
+
"exclude": ["node_modules"]
|
|
20
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {},
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@sequenceholdings/artifact-studio": "^0.
|
|
12
|
+
"@sequenceholdings/artifact-studio": "^0.3.0",
|
|
13
13
|
"@sequenceholdings/atlas-ui": "^3.1.1",
|
|
14
14
|
"@tailwindcss/postcss": "^4.2.2",
|
|
15
15
|
"@tailwindcss/vite": "^4.2.2",
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { Duplex } from 'node:stream';
|
|
3
|
+
import { type TunnelLimits } from './peer.js';
|
|
4
|
+
import type { TunnelRelay } from './transport.js';
|
|
5
|
+
export interface TunnelLease {
|
|
6
|
+
sessionId: string;
|
|
7
|
+
epoch: number;
|
|
8
|
+
ownerId: string;
|
|
9
|
+
projectId: string;
|
|
10
|
+
environment: string;
|
|
11
|
+
expiresAt: number;
|
|
12
|
+
publicOrigin: string;
|
|
13
|
+
}
|
|
14
|
+
export interface TunnelOwner {
|
|
15
|
+
nodeId: string;
|
|
16
|
+
epoch: number;
|
|
17
|
+
expiresAt: number;
|
|
18
|
+
}
|
|
19
|
+
/** Production adapters must atomically fence epochs across nodes. Ingress routes
|
|
20
|
+
* to lookup().nodeId; this module never silently forwards to an arbitrary node. */
|
|
21
|
+
export interface TunnelOwnershipStore {
|
|
22
|
+
claim(lease: TunnelLease, nodeId: string): Promise<boolean>;
|
|
23
|
+
lookup(sessionId: string): Promise<TunnelOwner | null>;
|
|
24
|
+
release(sessionId: string, epoch: number, nodeId: string): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export interface GatewayAuthorization {
|
|
27
|
+
ownerId: string;
|
|
28
|
+
publicOrigin: string;
|
|
29
|
+
/** Signed, audience-scoped context minted by Atlas, never a user bearer. */
|
|
30
|
+
requestContext?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface TunnelBrokerOptions {
|
|
33
|
+
nodeId: string;
|
|
34
|
+
routing: TunnelOwnershipStore;
|
|
35
|
+
authorizeTunnel(request: IncomingMessage): Promise<TunnelLease | null>;
|
|
36
|
+
authorizeRequest(request: IncomingMessage, sessionId: string): Promise<GatewayAuthorization | null>;
|
|
37
|
+
validateLease(lease: TunnelLease): Promise<boolean>;
|
|
38
|
+
relay?: TunnelRelay;
|
|
39
|
+
resolveLease?(sessionId: string): Promise<TunnelLease | null>;
|
|
40
|
+
onTunnelClosed?(lease: TunnelLease): void;
|
|
41
|
+
limits?: Partial<TunnelLimits>;
|
|
42
|
+
}
|
|
43
|
+
/** Transport only: all identity, cookie bootstrap and FGA checks belong to the
|
|
44
|
+
* mandatory Atlas callbacks. An unconfigured broker cannot accept connections. */
|
|
45
|
+
export declare class ArtifactTunnelBroker {
|
|
46
|
+
private readonly options;
|
|
47
|
+
private readonly connections;
|
|
48
|
+
private readonly limits;
|
|
49
|
+
private readonly tunnels;
|
|
50
|
+
private readonly pendingConnections;
|
|
51
|
+
private readonly ownerTimers;
|
|
52
|
+
private closing;
|
|
53
|
+
constructor(options: TunnelBrokerOptions);
|
|
54
|
+
handleTunnelUpgrade(request: IncomingMessage, socket: Duplex, head: Buffer): Promise<void>;
|
|
55
|
+
handleHttp(request: IncomingMessage, response: ServerResponse, sessionId: string): Promise<void>;
|
|
56
|
+
handleWebSocketUpgrade(request: IncomingMessage, socket: Duplex, head: Buffer, sessionId: string): Promise<void>;
|
|
57
|
+
private authorize;
|
|
58
|
+
private valid;
|
|
59
|
+
private remoteConnection;
|
|
60
|
+
private attach;
|
|
61
|
+
private receive;
|
|
62
|
+
private finish;
|
|
63
|
+
private abort;
|
|
64
|
+
/** Immediate revocation and graceful service shutdown hook. */
|
|
65
|
+
disconnect(sessionId: string): void;
|
|
66
|
+
close(): void;
|
|
67
|
+
}
|
|
68
|
+
/** For one-process local development/tests only. Production must provide a shared
|
|
69
|
+
* compare-and-swap store and route ingress to the claimed owner. */
|
|
70
|
+
export declare class SingleProcessTunnelOwnership implements TunnelOwnershipStore {
|
|
71
|
+
private readonly owners;
|
|
72
|
+
claim(lease: TunnelLease, nodeId: string): Promise<boolean>;
|
|
73
|
+
lookup(sessionId: string): Promise<TunnelOwner | null>;
|
|
74
|
+
release(sessionId: string, epoch: number, nodeId: string): Promise<void>;
|
|
75
|
+
}
|