Skip to content

Commit 2ce59a9

Browse files
fix(add): the playground builds the page's remote before running (#1230)
The backend answered 404 for /assets/mf-manifest.json — the manifest it advertises in its own /.well-known/solution.json — so a host could federate nothing and the page had no bundle to load. The cause was not the address. The solution runtime serves /assets/ from ASSETS_DIR, which already defaults to ../fe-remote/dist relative to the backend; there was simply never a build there. The fleet's playground runs its build-frontend.sh FIRST for exactly this reason, and the scaffolded Makefile did not build the remote at all. It now builds it and asserts the manifest exists before the run, so the failure is a build error at the top rather than a 404 an hour later. The shell installs the remote as a copy, so it needs it built too. Verified on a clean scaffold: dist/mf-manifest.json 1.7 kB backend /assets/mf-manifest.json 200 {"id":"notes","exposes":["Page"]} edge / 200 (signed in as alex) edge /v1/notes/overview 200 Co-authored-by: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent ea665d0 commit 2ce59a9

3 files changed

Lines changed: 44 additions & 4 deletions

File tree

‎cmd/add/solution_playground.go‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ FRONTEND=${endpoint:%s/web/http}
191191
// playgroundMakefile writes the run line, whose one non-obvious part is that
192192
// authority/edge is NAMED. Nothing starts it otherwise.
193193
func playgroundMakefile(name string) string {
194-
return fmt.Sprintf(`.PHONY: playground
194+
return fmt.Sprintf(`.PHONY: playground remote
195195
196196
# %s on the development authority, as a solution runs in a cell with no saas
197197
# host: the authority's edge is the one origin the browser opens and signs a
@@ -204,8 +204,19 @@ func playgroundMakefile(name string) string {
204204
# origin that can sign anyone in.
205205
PLAYGROUND_SERVICES = authority/edge %s/web %s/backend
206206
207-
playground:
207+
# THE REMOTE IS BUILT FIRST, and that is not a convenience. The solution
208+
# runtime serves /assets/ from ASSETS_DIR, which defaults to
209+
# ../fe-remote/dist relative to the backend — so with no build there, the
210+
# backend answers 404 for /assets/mf-manifest.json, the manifest it
211+
# advertises in /.well-known/solution.json, and a host can federate nothing.
212+
# The shell also installs the remote as a copy, so it needs it built.
213+
playground: remote
208214
cd %s && codefly run service $(PLAYGROUND_SERVICES) --env local
215+
216+
.PHONY: remote
217+
remote:
218+
cd services/fe-remote && npm install --no-audit --no-fund && npm run build
219+
test -f services/fe-remote/dist/mf-manifest.json
209220
`, titleFromName(name), name, name, playgroundDir)
210221
}
211222

‎cmd/add/solution_test.go‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,3 +555,25 @@ func TestTheContractDeclaresARoleBesidesInstaller(t *testing.T) {
555555
t.Fatalf("the contract must declare installer and viewer, got %v", names)
556556
}
557557
}
558+
559+
// TestThePlaygroundBuildsTheRemoteFirst. The solution runtime serves /assets/
560+
// from ASSETS_DIR, which defaults to ../fe-remote/dist relative to the
561+
// backend. With nothing built there the backend answers 404 for
562+
// /assets/mf-manifest.json — the manifest it advertises in
563+
// /.well-known/solution.json — and a host can federate nothing. The fleet's
564+
// own playground builds the remote before the run for exactly this reason.
565+
func TestThePlaygroundBuildsTheRemoteFirst(t *testing.T) {
566+
makefile := playgroundMakefile("notes")
567+
if !strings.Contains(makefile, "playground: remote") {
568+
t.Fatalf("the playground must build the remote first:\n%s", makefile)
569+
}
570+
for _, required := range []string{
571+
"cd services/fe-remote && npm install",
572+
"npm run build",
573+
"test -f services/fe-remote/dist/mf-manifest.json",
574+
} {
575+
if !strings.Contains(makefile, required) {
576+
t.Fatalf("the remote target must contain %q", required)
577+
}
578+
}
579+
}

‎docs/commands.md‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2435,8 +2435,9 @@ What it creates:
24352435
`services/fe-remote` — the `./Page` remote itself — is written by the solution
24362436
agent, not by this command: it is an npm package, not a Codefly service.
24372437

2438-
**Running it.** `make playground` starts the development authority and this
2439-
solution, and prints the one address a browser opens:
2438+
**Running it.** `make playground` builds the page's remote, starts the
2439+
development authority and this solution, and prints the one address a browser
2440+
opens:
24402441

24412442
```
24422443
edge listening on :14581, routing 1 module(s)
@@ -2450,6 +2451,12 @@ not behind its sign-in edge") and the authority's REST port (every browser
24502451
path 404s). In a cell the host's gateway plays the edge's part; the playground
24512452
is local only.
24522453

2454+
The remote is built **before** the run, and that is load-bearing: the solution
2455+
runtime serves `/assets/` from `ASSETS_DIR`, which defaults to
2456+
`../fe-remote/dist` relative to the backend. With nothing built there the
2457+
backend answers 404 for `/assets/mf-manifest.json` — the manifest it
2458+
advertises in `/.well-known/solution.json` — and a host can federate nothing.
2459+
24532460
Sign in as any of the fixture's acme people in a team — `maya`, `remi`,
24542461
`alex`, `dev` — and the solution's route answers:
24552462

0 commit comments

Comments
 (0)