@sequenceholdings/artifact-studio 0.2.5 → 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/README.md +35 -0
- package/VERSION-PIN.md +14 -3
- package/dist/api.d.ts +11 -1
- package/dist/api.js +26 -9
- package/dist/atlas-ui-css-plugin.d.ts +11 -7
- package/dist/atlas-ui-css-plugin.js +38 -10
- package/dist/build.d.ts +2 -2
- package/dist/build.js +14 -6
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +108 -37
- 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 +124 -19
- package/dist/manifest.js +12 -118
- 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 +420 -1
- package/dist/sdk.js +35 -0
- 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/artifact.bundle.yml +0 -1
- 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 +68 -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/artifact.bundle.yml +0 -1
- package/templates/react-vite/package.json +1 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Next.js on Artifact Studio
|
|
2
|
+
|
|
3
|
+
This app uses Next.js App Router, React 19 and Node 24. Its source and development
|
|
4
|
+
server live on your machine; Atlas manages access, previews and deployment. You
|
|
5
|
+
do not need GCP credentials or a cloud devbox.
|
|
6
|
+
|
|
7
|
+
## Get started
|
|
8
|
+
|
|
9
|
+
Install Node 24, pnpm and a released `seq-studio` version with Next.js support.
|
|
10
|
+
Your Atlas environment must have the Next.js runtime enabled.
|
|
11
|
+
|
|
12
|
+
From this directory:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
pnpm install
|
|
16
|
+
seq-studio envs list
|
|
17
|
+
seq-studio login --env <your-environment>
|
|
18
|
+
seq-studio artifact link --env <your-environment>
|
|
19
|
+
seq-studio artifact dev --env <your-environment>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Use an environment shown by `envs list`; for an OpCo, log in to that environment's
|
|
23
|
+
realm. The CLI opens an owner-only preview in Atlas and connects it to Next.js
|
|
24
|
+
over an authenticated outbound tunnel. Keep the process running while you edit.
|
|
25
|
+
Use Ctrl+C to stop it. When your machine is offline, the preview is offline too.
|
|
26
|
+
|
|
27
|
+
Local development uses Webpack with native Fast Refresh. This is the supported
|
|
28
|
+
bundler for the pinned Next.js release; it preserves client navigation during
|
|
29
|
+
updates through the Atlas preview. Dependency changes restart the
|
|
30
|
+
local server; linked package edits rebuild and restart it. Changes to
|
|
31
|
+
`artifact.bundle.yml` end the current session—restart `artifact dev` to authorize
|
|
32
|
+
the new configuration and permissions.
|
|
33
|
+
|
|
34
|
+
Commit the generated `pnpm-lock.yaml` with the app source. The workspace policy
|
|
35
|
+
keeps the seven-day dependency age gate and explicit install-script decisions.
|
|
36
|
+
Do not commit registry credentials or a project `.npmrc`.
|
|
37
|
+
|
|
38
|
+
For a standalone UI check without Atlas, run `pnpm dev` and open the loopback URL.
|
|
39
|
+
Atlas SDK calls require an authorized Atlas development session. The CLI sets
|
|
40
|
+
`ARTIFACT_STUDIO_DEV_ORIGIN` for the exact preview host; do not replace it with a
|
|
41
|
+
wildcard. Native Next.js routing is used throughout—no hash router or `basePath`.
|
|
42
|
+
|
|
43
|
+
## What to edit
|
|
44
|
+
|
|
45
|
+
| File | Purpose |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `app/page.tsx` | Request-time Server Component and client interaction examples |
|
|
48
|
+
| `app/actions.ts` | Validated server action; this example stores no data |
|
|
49
|
+
| `app/api/health/route.ts` | App-owned route handler |
|
|
50
|
+
| `app/providers.tsx` | Client boundary for the Atlas bridge, UI portals and React Query |
|
|
51
|
+
| `app/about/page.tsx` | Nested route for exercising Atlas and Next navigation |
|
|
52
|
+
| `artifact.bundle.yml` | Browser/server permissions and hosted runtime requirements |
|
|
53
|
+
|
|
54
|
+
`app/layout.tsx` verifies the gateway's request context and passes only safe UI
|
|
55
|
+
bootstrap data into the browser. Import Atlas UI components through
|
|
56
|
+
`@sequenceholdings/atlas-ui/next`, which preserves their React client boundary.
|
|
57
|
+
Keep `@sequenceholdings/artifact-studio/server` imports in
|
|
58
|
+
Server Components, server actions or route handlers. Framework route/layout and
|
|
59
|
+
configuration files use the default exports required by Next.js; other modules
|
|
60
|
+
use named exports.
|
|
61
|
+
|
|
62
|
+
Try incrementing the counter, then changing its label in
|
|
63
|
+
`app/components/counter.tsx`. Compatible edits preserve its value through Fast
|
|
64
|
+
Refresh. Submit the greeting form to exercise a server action; server validation
|
|
65
|
+
still applies when browser validation is bypassed.
|
|
66
|
+
|
|
67
|
+
## Data and secrets
|
|
68
|
+
|
|
69
|
+
Browser permissions and server permissions are separate in the manifest. Add
|
|
70
|
+
only the capabilities the application needs, then configure authorized resource
|
|
71
|
+
bindings in Atlas. Local development uses explicit nonproduction resources and
|
|
72
|
+
never falls back to production when a binding is missing.
|
|
73
|
+
|
|
74
|
+
Managed secrets stay in hosted runtimes. For local integration tests, use your
|
|
75
|
+
own test credentials, mocks or an existing governed function. Use a hosted
|
|
76
|
+
preview for application code that needs a managed secret. Never put secrets in
|
|
77
|
+
`build.public_env`, `NEXT_PUBLIC_*`, client components or committed files.
|
|
78
|
+
|
|
79
|
+
The local process is ordinary code running as your operating-system user.
|
|
80
|
+
Atlas governs access to platform resources; it does not sandbox your laptop's
|
|
81
|
+
filesystem or all outbound network connections.
|
|
82
|
+
|
|
83
|
+
## Build and deploy
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
pnpm type-check
|
|
87
|
+
pnpm build
|
|
88
|
+
seq-studio artifact plan --env <your-environment>
|
|
89
|
+
seq-studio artifact deploy --repo <namespace>/<repository> --ref main --env <your-environment>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Push the reviewed application source and lockfile to its Git Service repository
|
|
93
|
+
before deploying. The local build is a validation step. Atlas performs the official isolated build
|
|
94
|
+
from recorded source and a lockfile, then operates the hosted Next.js server.
|
|
95
|
+
Deployments do not depend on your laptop staying online. Reviewers use hosted
|
|
96
|
+
previews rather than your owner-only local session.
|
|
97
|
+
|
|
98
|
+
For official releases to deployed environments, use a clean reviewed `main`
|
|
99
|
+
revision under the existing Artifact deployment policy. Inspect build/deployment
|
|
100
|
+
status, permissions and runtime diagnostics in Artifact Studio. Failed runtime
|
|
101
|
+
checks do not replace the previous healthy release.
|
|
102
|
+
|
|
103
|
+
The default scaffold is dynamic and declares no distributed cache. Enable managed
|
|
104
|
+
cache capacity in Atlas before adopting framework features that require shared
|
|
105
|
+
cross-replica caching; local Fast Refresh is not a test of distributed cache
|
|
106
|
+
behavior. Do not add a production cache endpoint or managed credentials to local
|
|
107
|
+
environment files.
|
|
108
|
+
|
|
109
|
+
## Toolchain updates
|
|
110
|
+
|
|
111
|
+
This scaffold pins Next.js `16.3.4`, React/React DOM `19.2.6` and pnpm `10.28.2`,
|
|
112
|
+
with Node major `24`. These are its selected versions, not a claim that they are
|
|
113
|
+
the latest security releases. Use your platform's reviewed compatible versions
|
|
114
|
+
when updating an existing app. The CLI and build path check the platform's exact
|
|
115
|
+
approved Next/React selection; a different patch needs a coordinated platform
|
|
116
|
+
update, even if it belongs to the same framework profile.
|
|
117
|
+
|
|
118
|
+
For an approved patch, update the dependency declarations and lockfile together,
|
|
119
|
+
run the local type/build checks, then verify a hosted preview before requesting
|
|
120
|
+
an official repository-backed deployment. Existing immutable deployments keep
|
|
121
|
+
their previous code until rebuilt and activated. Your platform team maintains
|
|
122
|
+
build/runtime images and handles any urgent security response; this workflow
|
|
123
|
+
does not require application authors to obtain cloud-provider credentials.
|
|
124
|
+
|
|
125
|
+
## Images and fonts
|
|
126
|
+
|
|
127
|
+
Keep static images and other binary assets in `public/`. Prefer committed WOFF2
|
|
128
|
+
fonts loaded with `next/font/local`; compilation runs without unrestricted
|
|
129
|
+
network access.
|
|
130
|
+
|
|
131
|
+
For a public font prepared by the platform, add `build.public_fonts` entries with
|
|
132
|
+
`path`, `url` and the actual lowercase `sha256` digest. Targets must be
|
|
133
|
+
`public/fonts/<filename>.woff2`, without nested directories. Use ASCII
|
|
134
|
+
letters/digits, hyphens or underscores for the basename, starting with a letter
|
|
135
|
+
or digit. URLs must use HTTPS on `fonts.gstatic.com` with its versioned
|
|
136
|
+
`/s/<family>/v<revision>/<file>.woff2` format and no query, fragment or custom
|
|
137
|
+
port. The platform allows
|
|
138
|
+
up to eight files, 2 MiB each and 8 MiB total, with no redirects. Connected local
|
|
139
|
+
development prepares the same files. Existing matching files are reused;
|
|
140
|
+
mismatches fail without overwriting your file. Load the result with
|
|
141
|
+
`next/font/local`. A plain `pnpm build` uses files already present locally and
|
|
142
|
+
does not prepare declared fonts. Do not use `next/font/google` to download during the
|
|
143
|
+
network-blocked compilation stage.
|
|
144
|
+
|
|
145
|
+
For a remote optimized image, add the exact HTTPS host to both Next's
|
|
146
|
+
`images.remotePatterns` and the manifest's `server.egress`. Narrow the pathname
|
|
147
|
+
to the content needed by the app. Wildcard hosts, custom ports, redirects,
|
|
148
|
+
custom loaders, SVG optimization and private-IP destinations are unsupported.
|
|
149
|
+
Remote bodies are capped at 10 MiB, or a lower configured limit. Keep the
|
|
150
|
+
`withArtifactNextConfig` wrapper in place and verify remote images in a hosted
|
|
151
|
+
preview under the application's actual egress policy.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import Link from 'next/link'
|
|
2
|
+
|
|
3
|
+
export default function AboutPage() {
|
|
4
|
+
return (
|
|
5
|
+
<main className="mx-auto max-w-3xl px-roomy py-10 sm:px-8">
|
|
6
|
+
<Link href="/" className="text-sm font-medium text-primary underline-offset-4 hover:underline">
|
|
7
|
+
Back to your application
|
|
8
|
+
</Link>
|
|
9
|
+
<h1 className="mb-6 mt-8 text-2xl font-semibold">One application, two runtimes</h1>
|
|
10
|
+
<div className="space-y-4 text-foreground-secondary">
|
|
11
|
+
<p>
|
|
12
|
+
Server Components, actions and route handlers execute on your local
|
|
13
|
+
Next.js process during development and in the managed runtime after deployment.
|
|
14
|
+
</p>
|
|
15
|
+
<p>
|
|
16
|
+
Client Components provide interactive UI in the browser. Import Atlas UI
|
|
17
|
+
components from a client module and use the server SDK only in server code.
|
|
18
|
+
</p>
|
|
19
|
+
<p>
|
|
20
|
+
Atlas controls access, resource bindings and deployment. Local development
|
|
21
|
+
uses explicit nonproduction bindings; managed secrets stay in hosted runtimes.
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
</main>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use server'
|
|
2
|
+
|
|
3
|
+
export interface GreetingState {
|
|
4
|
+
message: string
|
|
5
|
+
error: string | null
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function greet(
|
|
9
|
+
_previousState: GreetingState,
|
|
10
|
+
formData: FormData,
|
|
11
|
+
): Promise<GreetingState> {
|
|
12
|
+
const input = formData.get('name')
|
|
13
|
+
const name = typeof input === 'string' ? input.trim() : ''
|
|
14
|
+
if (name.length === 0 || name.length > 80) {
|
|
15
|
+
return { message: '', error: 'Enter a name between 1 and 80 characters.' }
|
|
16
|
+
}
|
|
17
|
+
return { message: `Hello, ${name}. Your server action ran successfully.`, error: null }
|
|
18
|
+
}
|
|
@@ -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",
|