Skip to content

Add Serinus deployment guidance and Inertia frontend skill#3

Open
kingwill101 wants to merge 3 commits into
mainfrom
skill/inertia-dart-frontend
Open

Add Serinus deployment guidance and Inertia frontend skill#3
kingwill101 wants to merge 3 commits into
mainfrom
skill/inertia-dart-frontend

Conversation

@kingwill101

@kingwill101 kingwill101 commented Apr 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add direct Serinus asset serving for the serinus_inertia example, including Vite build updates and SSR env wiring
  • add a deployment guide, Dockerfiles, and Compose examples covering direct asset serving plus optional nginx/Caddy asset delivery
  • add a portable inertia-dart-frontend skill that works across inertia_dart, routed_inertia, and serinus_inertia without assuming this repo's local examples exist

Commit breakdown

  • feat(serinus_inertia): serve production assets directly
  • docs(serinus_inertia): add deployment guide and containers
  • feat(skills): add portable inertia frontend skill

Verification

  • dart analyze in packages/serinus_inertia/example
  • python3 /home/kingwill101/.codex/skills/skill-creator/scripts/quick_validate.py skills/inertia-dart-frontend
  • docker compose --profile client up --build -d
  • docker compose --profile ssr up --build -d
  • verified first-page loads, Inertia JSON responses, direct asset serving from Serinus, cache headers, and SSR-rendered HTML

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Docker and Docker Compose support for containerized deployments with separate client and SSR profiles
    • Introduced improved asset serving with immutable caching headers for production builds
    • Added comprehensive production deployment guide with step-by-step instructions
  • Documentation

    • Added detailed deployment guide covering production builds, systemd integration, and troubleshooting
    • Expanded README with Docker Compose workflows and production build instructions
    • Added skill guides for building and extending Inertia Dart frontends with client-side patterns, SSR, and deployment guidance
  • Chores

    • Added .dockerignore to exclude build artifacts from Docker context
    • Updated build scripts for improved client and SSR asset generation

@coderabbitai

coderabbitai Bot commented Apr 18, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds production-ready deployment infrastructure for a Serinus Inertia example project. It introduces Docker containerization (multi-stage Dockerfiles for client and SSR builds), Docker Compose orchestration with health checks, comprehensive deployment documentation, static asset serving with caching, and extensive skill documentation for building Inertia Dart frontends with multiple server integrations.

Changes

Cohort / File(s) Summary
Docker & Containerization
.dockerignore, Dockerfile.client, Dockerfile.ssr, compose.yaml
Added .dockerignore excluding build artifacts and dev dependencies. Created multi-stage Dockerfiles for client-app and SSR renderer builds (Node client + Dart server compilation into runtime image). Added Docker Compose configuration with three services (client-app, ssr-renderer, ssr-app) using profiles, health checks, and container-to-container networking for SSR endpoint wiring.
Deployment Documentation
packages/serinus_inertia/example/DEPLOY.md
New comprehensive deployment guide covering production build layout (Vite manifest, hashed assets, SSR bundle), Dart server compilation, runtime directory structure, static route mapping with cache headers, step-by-step assembly/runtime instructions, systemd service definitions, reverse proxy configurations, Docker Compose usage, smoke tests, and troubleshooting.
Example App Configuration & Build
packages/serinus_inertia/example/README.md, client/package.json, client/vite.config.js
Updated README with corrected option type names, expanded production build section referencing DEPLOY.md, and added Docker Compose workflow documentation. Split client build into build:client and build:ssr scripts; updated build:ssr to preserve output directory and emit SSR manifest. Added explicit Rollup input constraint to Vite config (src/main.jsx).
Dart Server-Side Asset & SSR Handling
lib/app_module.dart, lib/asset_controller.dart, pubspec.yaml
Added AssetController for serving static assets from client/dist/assets/ with file validation, MIME type detection, long-lived cache headers, and support for GET/HEAD requests. Enhanced app_module.dart with environment-driven SSR configuration resolution and wired it into InertiaOptions. Added mime package dependency (^2.0.0).
Skill Documentation & AI Agent
skills/inertia-dart-frontend/SKILL.md, skills/inertia-dart-frontend/agents/openai.yaml, skills/inertia-dart-frontend/references/*
Created new Inertia Dart Frontend skill guide and OpenAI agent interface. Added five reference documents covering frontend client patterns (adapters, entry files, resolver configuration), dart:io low-level integration, deployment shape and constraints, Docker best practices and patterns, and integration-specific guidance for routed_inertia and Serinus server frameworks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Hops of joy for assets served,
Docker containers, well preserved,
SSR renderers dance with Dart and Node,
A deployment guide shows the perfect road,
From dev to prod—the bunny's ode! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: adding Serinus deployment guidance and a portable Inertia frontend skill, matching the core objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch skill/inertia-dart-frontend

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (5)
packages/serinus_inertia/example/client/vite.config.js (1)

15-17: Consider aligning with the InertiaAssetsOptions.entry.

rollupOptions.input: 'src/main.jsx' duplicates the entry declared in app_module.dart (InertiaAssetsOptions(entry: 'src/main.jsx', ...)). If one side changes (e.g., to src/ssr.jsx or a different entry) the other can silently get out of sync and break manifest lookups. Not a blocker, but worth a brief comment linking the two, or parameterizing via an env var in a follow-up.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/serinus_inertia/example/client/vite.config.js` around lines 15 - 17,
The rollupOptions.input in vite.config.js duplicates the entry declared in
InertiaAssetsOptions(entry: 'src/main.jsx') and can drift out of sync; update
vite.config.js to derive input from a single source (e.g., read an env var like
VITE_ENTRY or import a shared constant) or at minimum add a clear comment
linking it to InertiaAssetsOptions(entry: ...) so maintainers know they must
change both; reference rollupOptions.input and InertiaAssetsOptions(entry: ...)
when making the change.
packages/serinus_inertia/example/Dockerfile.client (1)

3-19: Both build stages COPY . . the entire repo — consider scoping.

Each build stage copies the full monorepo before narrowing to packages/serinus_inertia/example. This invalidates the Docker layer cache on any unrelated file change and inflates build context. Since serinus_inertia depends on the sibling inertia package via path: override, the copy can't be trivially reduced to one directory, but you can at least split the copies so npm ci/dart pub get layers cache on manifest files first.

♻️ Suggested cache-friendlier ordering (client stage)
 FROM node:24-bookworm-slim AS client-build
 WORKDIR /workspace

-COPY . .
-
-WORKDIR /workspace/packages/serinus_inertia/example/client
-RUN npm ci
-RUN npm run build
+COPY packages/serinus_inertia/example/client/package.json packages/serinus_inertia/example/client/package-lock.json ./packages/serinus_inertia/example/client/
+WORKDIR /workspace/packages/serinus_inertia/example/client
+RUN npm ci
+WORKDIR /workspace
+COPY . .
+WORKDIR /workspace/packages/serinus_inertia/example/client
+RUN npm run build

Feel free to defer; purely a build-time optimization.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/serinus_inertia/example/Dockerfile.client` around lines 3 - 19, The
Dockerfile uses COPY . . in both client-build and server-build stages which
copies the whole monorepo and busts caches; change each stage to first copy only
manifest files needed for dependency installation (e.g., package.json,
package-lock.json for the client stage used before RUN npm ci and pubspec.*
files for the server stage used before RUN dart pub get) then run the install
steps (npm ci / dart pub get) and finally COPY the rest of the workspace before
RUN npm run build and dart compile in the client-build and server-build stages
respectively; update the client-build and server-build stages to reference these
staged copies so npm ci and dart pub get layers cache properly while still
including the sibling package source before the final build commands.
packages/serinus_inertia/example/lib/app_module.dart (1)

22-38: Minor: env-driven SSR resolver ignores SERINUS_INERTIA_SSR_RUNTIME/SERINUS_INERTIA_SSR_BUNDLE when an endpoint is also set.

If a user sets both SERINUS_INERTIA_SSR_ENDPOINT and the managed-process vars, the endpoint silently wins and the managed-process vars are discarded with no warning. That's a defensible precedence rule, but consider documenting it in the deploy guide (or logging a hint) so misconfigured environments aren't confusing to debug.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/serinus_inertia/example/lib/app_module.dart` around lines 22 - 38,
The env-driven SSR resolver currently returns the endpoint silently when
SERINUS_INERTIA_SSR_ENDPOINT is set, discarding SERINUS_INERTIA_SSR_RUNTIME and
SERINUS_INERTIA_SSR_BUNDLE; update _resolveSsrOptions to detect when endpoint !=
null and either SERINUS_INERTIA_SSR_RUNTIME or SERINUS_INERTIA_SSR_BUNDLE are
also set and emit a clear warning that the endpoint takes precedence (use the
existing logging facility, e.g., processLogger.warn or a print fallback), so
users see the precedence; also add a short note to the deploy docs explaining
this precedence rule.
skills/inertia-dart-frontend/references/client-patterns.md (1)

53-58: Make component-path examples adapter-agnostic.

These examples currently imply React-only extensions in a shared multi-adapter guide. Consider using .* or showing one example per adapter to avoid confusion.

🧩 Suggested example tweak
-- `component: 'Home'` -> `client/src/Pages/Home.jsx`
-- `component: 'Users/Index'` -> `client/src/Pages/Users/Index.jsx`
+- `component: 'Home'` -> `client/src/Pages/Home.*` (e.g., `.jsx`, `.vue`, `.svelte`)
+- `component: 'Users/Index'` -> `client/src/Pages/Users/Index.*`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/inertia-dart-frontend/references/client-patterns.md` around lines 53 -
58, The examples for the Dart-side `component` path currently hardcode a React
extension (`.jsx`) and should be adapter-agnostic: update the two sample lines
that map `component` to `client/src/Pages/...` (the examples referencing
`component: 'Home'` and `component: 'Users/Index'`) to either use a generic
extension wildcard (e.g., `.*`) or show one example per adapter (e.g., `.jsx`
for React, `.dart` for Dart, etc.), so the documentation for `component` clearly
indicates the file path without implying a single frontend adapter.
skills/inertia-dart-frontend/SKILL.md (1)

45-48: Reduce repeated imperative openings in list items.

The repeated “Use …” / “Read …” pattern in adjacent bullets is a small readability drag; varying phrasing would improve scan speed without changing meaning.

Also applies to: 61-66

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/inertia-dart-frontend/SKILL.md` around lines 45 - 48, The adjacent
bullet list repeatedly starts lines with "Use …" which reduces readability;
revise the bullets mentioning LazyProp, OptionalProp, DeferredProp, MergeProp,
ScrollProp, and OnceProp to vary phrasing (e.g., "Prefer
LazyProp/OptionalProp…", "Reserve DeferredProp…", "Apply MergeProp/ScrollProp…",
"Choose OnceProp…") and make the same phrasing adjustments for the similar block
at 61-66 to remove repetitive openings while preserving meanings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/serinus_inertia/example/client/package.json`:
- Around line 8-10: The SSR preload manifest is being generated in the SSR
build; update the npm scripts so the SSR manifest is produced during the client
build instead: remove the --manifest ssr-manifest.json flag from the "build:ssr"
script and add the --ssrManifest ssr-manifest.json flag to the "build:client"
script (or alternatively set build.ssrManifest in Vite config); adjust the
"build" script unchanged to run "build:client" then "build:ssr". This targets
the scripts named build:client and build:ssr.

In `@packages/serinus_inertia/example/DEPLOY.md`:
- Around line 398-402: The bullets in DEPLOY.md repeat the same lead phrasing
making them hard to scan; update the five checks to vary the sentence starters
while keeping the same verification items (references: client/public/hot,
client/dist/assets, nginx/Caddy proxy root -> client/dist,
client/dist/.vite/manifest.json, client/dist/ssr.js and /render). For example,
change each bullet to start differently (e.g., "Remove...", "Verify...", "Ensure
your proxy root...", "Confirm the manifest...", "When SSR is enabled, check...")
so each line is concise, action-oriented, and still instructs the same checks
without altering the file paths or intent.

In `@packages/serinus_inertia/example/Dockerfile.client`:
- Around line 21-33: The runtime Dockerfile app stage currently runs as root;
add a non-root user and switch to it before CMD to satisfy Trivy DS-0002. In the
final stage (labels: FROM debian:bookworm-slim AS app, WORKDIR
/opt/serinus-inertia, EXPOSE 4000, CMD ["./server"]) create a dedicated
user/group (e.g., uid/gid 1000), chown the application directories
(/opt/serinus-inertia, ./server, ./client/dist) to that user, use USER to switch
to the non-root user, and ensure the server binary has execute permission so CMD
["./server"] runs as the non-root user.

In `@packages/serinus_inertia/example/Dockerfile.ssr`:
- Around line 21-48: The final image stages (app and ssr) run as root; update
both stages (the "app" stage and the "ssr" stage) to create a non-root
user/group (e.g., uid/gid 1000), chown the application directories and copied
artifacts (the server binary at ./server and the client dist at ./client/dist or
./dist), and add USER to switch to that non-root account before EXPOSE/CMD so
the app listens on ports 4000 and 13714 unprivileged; ensure files are
executable and owned by the new user after the COPY steps and before switching
USER.

In `@packages/serinus_inertia/example/lib/asset_controller.dart`:
- Around line 20-28: The code currently uses synchronous filesystem calls
(assetFile.existsSync(), assetFile.statSync(), assetFile.lengthSync()) in the
request path; replace them with a single asynchronous stat call: call await
assetFile.stat(), check the returned FileStat.type equals
FileSystemEntityType.file (throw NotFoundException('Asset not found') if not),
then reuse FileStat.size to set context.res.contentLength and keep setting
context.res.contentType as before (using lookupMimeType on assetFile.path).
Update any surrounding function to be async if needed so you can await
assetFile.stat().
- Around line 14-19: The _containsUnsafeSegments check currently only splits
relativePath on '/' so backslashes can hide traversal; update
_containsUnsafeSegments to split the incoming relativePath on both '/' and '\'
(treat both as separators), reject any empty segments, and then check each
segment for '.' or '..' (or any other unsafe token) to return true; ensure the
code that reads context.params['**'] (relativePath) still uses this updated
helper before constructing File('client/dist/assets/$relativePath') so
Windows-style separators cannot bypass the validation.

In `@skills/inertia-dart-frontend/references/dart-io.md`:
- Around line 21-28: Add the Inertia asset-version conflict check before
building a normal page response: when handling an Inertia request
(inertiaRequest.isInertia), compare the request version (inertiaRequest.version)
to the current app asset version (the same check logic used in
RoutedInertiaMiddleware), and if they differ return an HTTP 409 with an
X-Inertia-Location header pointing to the current location instead of calling
InertiaResponseFactory().buildPageDataAsync(...) or
renderInertiaBootstrap(page); ensure this early-return happens immediately after
inertiaRequestFromHttp(request) / inertiaRequest.createContext() and before any
page construction or JSON/html rendering.

---

Nitpick comments:
In `@packages/serinus_inertia/example/client/vite.config.js`:
- Around line 15-17: The rollupOptions.input in vite.config.js duplicates the
entry declared in InertiaAssetsOptions(entry: 'src/main.jsx') and can drift out
of sync; update vite.config.js to derive input from a single source (e.g., read
an env var like VITE_ENTRY or import a shared constant) or at minimum add a
clear comment linking it to InertiaAssetsOptions(entry: ...) so maintainers know
they must change both; reference rollupOptions.input and
InertiaAssetsOptions(entry: ...) when making the change.

In `@packages/serinus_inertia/example/Dockerfile.client`:
- Around line 3-19: The Dockerfile uses COPY . . in both client-build and
server-build stages which copies the whole monorepo and busts caches; change
each stage to first copy only manifest files needed for dependency installation
(e.g., package.json, package-lock.json for the client stage used before RUN npm
ci and pubspec.* files for the server stage used before RUN dart pub get) then
run the install steps (npm ci / dart pub get) and finally COPY the rest of the
workspace before RUN npm run build and dart compile in the client-build and
server-build stages respectively; update the client-build and server-build
stages to reference these staged copies so npm ci and dart pub get layers cache
properly while still including the sibling package source before the final build
commands.

In `@packages/serinus_inertia/example/lib/app_module.dart`:
- Around line 22-38: The env-driven SSR resolver currently returns the endpoint
silently when SERINUS_INERTIA_SSR_ENDPOINT is set, discarding
SERINUS_INERTIA_SSR_RUNTIME and SERINUS_INERTIA_SSR_BUNDLE; update
_resolveSsrOptions to detect when endpoint != null and either
SERINUS_INERTIA_SSR_RUNTIME or SERINUS_INERTIA_SSR_BUNDLE are also set and emit
a clear warning that the endpoint takes precedence (use the existing logging
facility, e.g., processLogger.warn or a print fallback), so users see the
precedence; also add a short note to the deploy docs explaining this precedence
rule.

In `@skills/inertia-dart-frontend/references/client-patterns.md`:
- Around line 53-58: The examples for the Dart-side `component` path currently
hardcode a React extension (`.jsx`) and should be adapter-agnostic: update the
two sample lines that map `component` to `client/src/Pages/...` (the examples
referencing `component: 'Home'` and `component: 'Users/Index'`) to either use a
generic extension wildcard (e.g., `.*`) or show one example per adapter (e.g.,
`.jsx` for React, `.dart` for Dart, etc.), so the documentation for `component`
clearly indicates the file path without implying a single frontend adapter.

In `@skills/inertia-dart-frontend/SKILL.md`:
- Around line 45-48: The adjacent bullet list repeatedly starts lines with "Use
…" which reduces readability; revise the bullets mentioning LazyProp,
OptionalProp, DeferredProp, MergeProp, ScrollProp, and OnceProp to vary phrasing
(e.g., "Prefer LazyProp/OptionalProp…", "Reserve DeferredProp…", "Apply
MergeProp/ScrollProp…", "Choose OnceProp…") and make the same phrasing
adjustments for the similar block at 61-66 to remove repetitive openings while
preserving meanings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3fe1a07d-6995-4c02-a838-208319f9bebd

📥 Commits

Reviewing files that changed from the base of the PR and between e548f76 and 75a7789.

📒 Files selected for processing (19)
  • .dockerignore
  • packages/serinus_inertia/example/DEPLOY.md
  • packages/serinus_inertia/example/Dockerfile.client
  • packages/serinus_inertia/example/Dockerfile.ssr
  • packages/serinus_inertia/example/README.md
  • packages/serinus_inertia/example/client/package.json
  • packages/serinus_inertia/example/client/vite.config.js
  • packages/serinus_inertia/example/compose.yaml
  • packages/serinus_inertia/example/lib/app_module.dart
  • packages/serinus_inertia/example/lib/asset_controller.dart
  • packages/serinus_inertia/example/pubspec.yaml
  • skills/inertia-dart-frontend/SKILL.md
  • skills/inertia-dart-frontend/agents/openai.yaml
  • skills/inertia-dart-frontend/references/client-patterns.md
  • skills/inertia-dart-frontend/references/dart-io.md
  • skills/inertia-dart-frontend/references/deployment.md
  • skills/inertia-dart-frontend/references/docker.md
  • skills/inertia-dart-frontend/references/routed.md
  • skills/inertia-dart-frontend/references/serinus.md

Comment on lines +8 to +10
"build:client": "node -e \"const fs=require('node:fs'); try { fs.unlinkSync('public/hot') } catch {}\" && vite build",
"build": "npm run build:client && npm run build:ssr",
"build:ssr": "vite build --ssr src/ssr.jsx --emptyOutDir false --manifest ssr-manifest.json",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Vite 7 official docs vite build --ssrManifest build.ssrManifest SSR manifest client build

💡 Result:

In Vite (including version 7), the vite build --ssrManifest build.ssrManifest command generates an SSR manifest file during the client build for use in server-side rendering (SSR) setups. This maps module IDs (collected during SSR rendering) to their corresponding client-side chunks and assets, enabling preload directives (), style links, and 103 Early Hints in production SSR servers. ## Usage Run on the client build (not SSR build): "build:client": "vite build --outDir dist/client --ssrManifest build.ssrManifest" - Generates dist/client/build.ssrManifest (custom filename via argument; default: .vite/ssr-manifest.json). - The SSR manifest is created from the client build because it maps SSR-used module IDs to client files. ## Config Alternative In vite.config.js: js export default defineConfig({ build: { ssrManifest: 'build.ssrManifest' // Path relative to build.outDir } }) - Type: boolean | string (default: false). - When true: .vite/ssr-manifest.json. ## In SSR Server 1. Read the manifest: const ssrManifest = JSON.parse(await fs.readFile('dist/client/build.ssrManifest', 'utf-8')). 2. During SSR render, collect used module IDs (framework-specific, e.g., @vitejs/plugin-vue populates ctx.modules). 3. Use manifest to generate preloads: js // Pseudo-code for (const id of ctx.modules) { const files = ssrManifest[id]; if (files) { for (const file of files) { if (file.endsWith('.js')) { html += ; } else if (file.endsWith('.css')) { html += ; } } } } ## Vite 7 Status - Fully supported (released June 2025); no breaking changes to SSR manifest in Vite 7 changelogs. - Vite 8 (beta as of 2026-02) uses Rolldown; minor manifest adjustments (e.g., CSS assets) but core SSR manifest unchanged. ## Example package.json Scripts { "scripts": { "build:client": "vite build --outDir dist/client --ssrManifest build.ssrManifest", "build:ssr": "vite build --outDir dist/server --ssr src/entry-server.js" } } See official docs: SSR Guide, Build Options.

Citations:


🏁 Script executed:

# Check the actual package.json file
cat -n packages/serinus_inertia/example/client/package.json

Repository: avesbox/inertia_dart

Length of output: 1185


🏁 Script executed:

# Look for Vite configuration files
fd -e js -e ts -e json --basename 'vite.config.*' packages/serinus_inertia/example/

Repository: avesbox/inertia_dart

Length of output: 326


🏁 Script executed:

# Check if there's a root vite.config
fd -e js -e ts -e json --basename 'vite.config.*' packages/serinus_inertia/

Repository: avesbox/inertia_dart

Length of output: 326


🏁 Script executed:

# Find vite config files using proper fd syntax
fd -e js -e ts 'vite\.config' packages/serinus_inertia/example/

Repository: avesbox/inertia_dart

Length of output: 118


🏁 Script executed:

# Also check parent directories
fd -e js -e ts 'vite\.config' packages/serinus_inertia/

Repository: avesbox/inertia_dart

Length of output: 118


🏁 Script executed:

# Check the vite.config.js contents
cat -n packages/serinus_inertia/example/client/vite.config.js

Repository: avesbox/inertia_dart

Length of output: 595


🏁 Script executed:

# Also check if there's an SSR entry file
cat -n packages/serinus_inertia/example/client/src/ssr.jsx

Repository: avesbox/inertia_dart

Length of output: 622


Move SSR manifest generation to the client build.

The --manifest ssr-manifest.json flag on the SSR build creates a regular build manifest, not Vite's SSR preload manifest. Per Vite 7 documentation, --ssrManifest (or build.ssrManifest in config) must run on the client build, since it maps SSR-used module IDs to client-side chunks for preload directives. Reference: https://vite.dev/guide/ssr#generating-preload-directives and https://vite.dev/config/build-options.html#build-ssrmanifest

Proposed fix
-    "build:client": "node -e \"const fs=require('node:fs'); try { fs.unlinkSync('public/hot') } catch {}\" && vite build",
+    "build:client": "node -e \"const fs=require('node:fs'); try { fs.unlinkSync('public/hot') } catch {}\" && vite build --ssrManifest ssr-manifest.json",
     "build": "npm run build:client && npm run build:ssr",
-    "build:ssr": "vite build --ssr src/ssr.jsx --emptyOutDir false --manifest ssr-manifest.json",
+    "build:ssr": "vite build --ssr src/ssr.jsx --emptyOutDir false",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"build:client": "node -e \"const fs=require('node:fs'); try { fs.unlinkSync('public/hot') } catch {}\" && vite build",
"build": "npm run build:client && npm run build:ssr",
"build:ssr": "vite build --ssr src/ssr.jsx --emptyOutDir false --manifest ssr-manifest.json",
"build:client": "node -e \"const fs=require('node:fs'); try { fs.unlinkSync('public/hot') } catch {}\" && vite build --ssrManifest ssr-manifest.json",
"build": "npm run build:client && npm run build:ssr",
"build:ssr": "vite build --ssr src/ssr.jsx --emptyOutDir false",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/serinus_inertia/example/client/package.json` around lines 8 - 10,
The SSR preload manifest is being generated in the SSR build; update the npm
scripts so the SSR manifest is produced during the client build instead: remove
the --manifest ssr-manifest.json flag from the "build:ssr" script and add the
--ssrManifest ssr-manifest.json flag to the "build:client" script (or
alternatively set build.ssrManifest in Vite config); adjust the "build" script
unchanged to run "build:client" then "build:ssr". This targets the scripts named
build:client and build:ssr.

Comment on lines +398 to +402
- If the app tries to load assets from a Vite dev server, remove `client/public/hot`.
- If assets 404, confirm the working directory contains `client/dist/assets`.
- If assets 404 behind nginx or Caddy, confirm the proxy root points to `client/dist`, not the deployment root.
- If first visits fail, confirm `client/dist/.vite/manifest.json` exists.
- If SSR is enabled, confirm `client/dist/ssr.js` exists and the renderer is reachable on `/render`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Tighten troubleshooting bullet phrasing for scanability.

These consecutive bullets repeat the same sentence opener, which makes the section harder to scan quickly during incidents. Consider varying the lead phrasing while keeping the checks intact.

✏️ Suggested wording cleanup
-- If the app tries to load assets from a Vite dev server, remove `client/public/hot`.
-- If assets 404, confirm the working directory contains `client/dist/assets`.
-- If assets 404 behind nginx or Caddy, confirm the proxy root points to `client/dist`, not the deployment root.
-- If first visits fail, confirm `client/dist/.vite/manifest.json` exists.
-- If SSR is enabled, confirm `client/dist/ssr.js` exists and the renderer is reachable on `/render`.
+- App loading from a Vite dev server in production usually means `client/public/hot` still exists; remove it.
+- For direct-serving 404s, verify the process working directory contains `client/dist/assets`.
+- For proxy-only 404s, verify nginx/Caddy points at `client/dist` (not the deployment root).
+- First-visit failures often indicate a missing `client/dist/.vite/manifest.json`.
+- With SSR enabled, verify `client/dist/ssr.js` exists and `/render` is reachable.
🧰 Tools
🪛 LanguageTool

[style] ~400-~400: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ectory contains client/dist/assets. - If assets 404 behind nginx or Caddy, confi...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~401-~401: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...lient/dist, not the deployment root. - If first visits fail, confirm client/dist...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~402-~402: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ent/dist/.vite/manifest.jsonexists. - If SSR is enabled, confirmclient/dist/ss...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/serinus_inertia/example/DEPLOY.md` around lines 398 - 402, The
bullets in DEPLOY.md repeat the same lead phrasing making them hard to scan;
update the five checks to vary the sentence starters while keeping the same
verification items (references: client/public/hot, client/dist/assets,
nginx/Caddy proxy root -> client/dist, client/dist/.vite/manifest.json,
client/dist/ssr.js and /render). For example, change each bullet to start
differently (e.g., "Remove...", "Verify...", "Ensure your proxy root...",
"Confirm the manifest...", "When SSR is enabled, check...") so each line is
concise, action-oriented, and still instructs the same checks without altering
the file paths or intent.

Comment on lines +21 to +33
FROM debian:bookworm-slim AS app
WORKDIR /opt/serinus-inertia

RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*

COPY --from=server-build /workspace/packages/serinus_inertia/example/build/server ./server
COPY --from=client-build /workspace/packages/serinus_inertia/example/client/dist ./client/dist

EXPOSE 4000

CMD ["./server"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Run the runtime container as a non-root user.

The final app stage runs as root by default, which Trivy flags (DS-0002) and is not needed for serving assets and listening on port 4000. Add a dedicated non-root user in the runtime stage.

🛡️ Proposed hardening
 FROM debian:bookworm-slim AS app
 WORKDIR /opt/serinus-inertia

 RUN apt-get update \
     && apt-get install -y --no-install-recommends ca-certificates \
-    && rm -rf /var/lib/apt/lists/*
+    && rm -rf /var/lib/apt/lists/* \
+    && groupadd --system --gid 1001 app \
+    && useradd  --system --uid 1001 --gid app --home /opt/serinus-inertia app

-COPY --from=server-build /workspace/packages/serinus_inertia/example/build/server ./server
-COPY --from=client-build /workspace/packages/serinus_inertia/example/client/dist ./client/dist
+COPY --from=server-build --chown=app:app /workspace/packages/serinus_inertia/example/build/server ./server
+COPY --from=client-build --chown=app:app /workspace/packages/serinus_inertia/example/client/dist ./client/dist

+USER app
 EXPOSE 4000

 CMD ["./server"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
FROM debian:bookworm-slim AS app
WORKDIR /opt/serinus-inertia
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY --from=server-build /workspace/packages/serinus_inertia/example/build/server ./server
COPY --from=client-build /workspace/packages/serinus_inertia/example/client/dist ./client/dist
EXPOSE 4000
CMD ["./server"]
FROM debian:bookworm-slim AS app
WORKDIR /opt/serinus-inertia
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd --system --gid 1001 app \
&& useradd --system --uid 1001 --gid app --home /opt/serinus-inertia app
COPY --from=server-build --chown=app:app /workspace/packages/serinus_inertia/example/build/server ./server
COPY --from=client-build --chown=app:app /workspace/packages/serinus_inertia/example/client/dist ./client/dist
USER app
EXPOSE 4000
CMD ["./server"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/serinus_inertia/example/Dockerfile.client` around lines 21 - 33, The
runtime Dockerfile app stage currently runs as root; add a non-root user and
switch to it before CMD to satisfy Trivy DS-0002. In the final stage (labels:
FROM debian:bookworm-slim AS app, WORKDIR /opt/serinus-inertia, EXPOSE 4000, CMD
["./server"]) create a dedicated user/group (e.g., uid/gid 1000), chown the
application directories (/opt/serinus-inertia, ./server, ./client/dist) to that
user, use USER to switch to the non-root user, and ensure the server binary has
execute permission so CMD ["./server"] runs as the non-root user.

Comment on lines +21 to +48
FROM debian:bookworm-slim AS app
WORKDIR /opt/serinus-inertia

RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*

COPY --from=server-build /workspace/packages/serinus_inertia/example/build/server ./server
COPY --from=client-build /workspace/packages/serinus_inertia/example/client/dist ./client/dist

EXPOSE 4000

CMD ["./server"]

FROM node:24-bookworm-slim AS ssr
WORKDIR /opt/serinus-inertia/client

COPY packages/serinus_inertia/example/client/package.json ./package.json
COPY packages/serinus_inertia/example/client/package-lock.json ./package-lock.json
RUN npm ci --omit=dev

COPY --from=client-build /workspace/packages/serinus_inertia/example/client/dist ./dist

WORKDIR /opt/serinus-inertia

EXPOSE 13714

CMD ["node", "./client/dist/ssr.js"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Run the runtime stages as non-root users.

Both final images currently default to root. The app listens on 4000 and the SSR renderer on 13714, so they can run unprivileged after artifacts are copied.

🛡️ Proposed hardening
 FROM debian:bookworm-slim AS app
 WORKDIR /opt/serinus-inertia
 
 RUN apt-get update \
     && apt-get install -y --no-install-recommends ca-certificates \
-    && rm -rf /var/lib/apt/lists/*
+    && rm -rf /var/lib/apt/lists/* \
+    && groupadd --system serinus \
+    && useradd --system --gid serinus --home-dir /opt/serinus-inertia --shell /usr/sbin/nologin serinus
 
-COPY --from=server-build /workspace/packages/serinus_inertia/example/build/server ./server
-COPY --from=client-build /workspace/packages/serinus_inertia/example/client/dist ./client/dist
+COPY --from=server-build --chown=serinus:serinus /workspace/packages/serinus_inertia/example/build/server ./server
+COPY --from=client-build --chown=serinus:serinus /workspace/packages/serinus_inertia/example/client/dist ./client/dist
 
 EXPOSE 4000
 
+USER serinus
+
 CMD ["./server"]
 
 FROM node:24-bookworm-slim AS ssr
 WORKDIR /opt/serinus-inertia/client
 
 COPY packages/serinus_inertia/example/client/package.json ./package.json
 COPY packages/serinus_inertia/example/client/package-lock.json ./package-lock.json
 RUN npm ci --omit=dev
 
 COPY --from=client-build /workspace/packages/serinus_inertia/example/client/dist ./dist
 
 WORKDIR /opt/serinus-inertia
 
 EXPOSE 13714
 
+RUN chown -R node:node /opt/serinus-inertia
+USER node
+
 CMD ["node", "./client/dist/ssr.js"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
FROM debian:bookworm-slim AS app
WORKDIR /opt/serinus-inertia
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY --from=server-build /workspace/packages/serinus_inertia/example/build/server ./server
COPY --from=client-build /workspace/packages/serinus_inertia/example/client/dist ./client/dist
EXPOSE 4000
CMD ["./server"]
FROM node:24-bookworm-slim AS ssr
WORKDIR /opt/serinus-inertia/client
COPY packages/serinus_inertia/example/client/package.json ./package.json
COPY packages/serinus_inertia/example/client/package-lock.json ./package-lock.json
RUN npm ci --omit=dev
COPY --from=client-build /workspace/packages/serinus_inertia/example/client/dist ./dist
WORKDIR /opt/serinus-inertia
EXPOSE 13714
CMD ["node", "./client/dist/ssr.js"]
FROM debian:bookworm-slim AS app
WORKDIR /opt/serinus-inertia
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd --system serinus \
&& useradd --system --gid serinus --home-dir /opt/serinus-inertia --shell /usr/sbin/nologin serinus
COPY --from=server-build --chown=serinus:serinus /workspace/packages/serinus_inertia/example/build/server ./server
COPY --from=client-build --chown=serinus:serinus /workspace/packages/serinus_inertia/example/client/dist ./client/dist
EXPOSE 4000
USER serinus
CMD ["./server"]
FROM node:24-bookworm-slim AS ssr
WORKDIR /opt/serinus-inertia/client
COPY packages/serinus_inertia/example/client/package.json ./package.json
COPY packages/serinus_inertia/example/client/package-lock.json ./package-lock.json
RUN npm ci --omit=dev
COPY --from=client-build /workspace/packages/serinus_inertia/example/client/dist ./dist
WORKDIR /opt/serinus-inertia
EXPOSE 13714
RUN chown -R node:node /opt/serinus-inertia
USER node
CMD ["node", "./client/dist/ssr.js"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/serinus_inertia/example/Dockerfile.ssr` around lines 21 - 48, The
final image stages (app and ssr) run as root; update both stages (the "app"
stage and the "ssr" stage) to create a non-root user/group (e.g., uid/gid 1000),
chown the application directories and copied artifacts (the server binary at
./server and the client dist at ./client/dist or ./dist), and add USER to switch
to that non-root account before EXPOSE/CMD so the app listens on ports 4000 and
13714 unprivileged; ensure files are executable and owned by the new user after
the COPY steps and before switching USER.

Comment on lines +14 to +19
final relativePath = context.params['**']?.toString() ?? '';
if (relativePath.isEmpty || _containsUnsafeSegments(relativePath)) {
throw NotFoundException('Asset not found');
}

final assetFile = File('client/dist/assets/$relativePath');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Verify that AssetController rejects both POSIX and Windows-style traversal segments.
rg -n -C3 "split\\(RegExp\\(r'\\[\\\\\\\\/\\\\\\\\\\]\\+'\\)|split\\('/'\\)|_containsUnsafeSegments" --glob 'asset_controller.dart'

Repository: avesbox/inertia_dart

Length of output: 1367


🏁 Script executed:

cat -n packages/serinus_inertia/example/lib/asset_controller.dart

Repository: avesbox/inertia_dart

Length of output: 1696


🏁 Script executed:

# Search for other path validation patterns or similar issues in the codebase
rg -n "split\('|split\(RegExp|File\(" packages/serinus_inertia/example/lib/ -A2 -B2

Repository: avesbox/inertia_dart

Length of output: 991


🏁 Script executed:

# Check if there are any other implementations or usage patterns of path handling
fd "controller" packages/serinus_inertia/example/lib/ -type f | head -20

Repository: avesbox/inertia_dart

Length of output: 234


🏁 Script executed:

# Check if serinus is a dependency and find its implementation
rg -n "class RequestContext|context.params\|" packages/serinus_inertia/ -A5 -B2 | head -50

Repository: avesbox/inertia_dart

Length of output: 46


🏁 Script executed:

# Search for how URL parameters are decoded in the framework
fd "serinus" packages/ -type d

Repository: avesbox/inertia_dart

Length of output: 234


🏁 Script executed:

# Check if there's any URL decoding happening in the codebase
rg -n "Uri.decodeComponent|Uri.parse|decodeURI|decodeURIComponent" packages/serinus_inertia/

Repository: avesbox/inertia_dart

Length of output: 1847


🏁 Script executed:

# Look for documentation or pubspec files to understand serinus dependency
cat packages/serinus_inertia/pubspec.yaml | head -40

Repository: avesbox/inertia_dart

Length of output: 577


🏁 Script executed:

# Search for tests of the AssetController or route parameter handling
rg -n "asset|/**|params\[" packages/serinus_inertia/example/ -A3 -B3 | head -60

Repository: avesbox/inertia_dart

Length of output: 4172


🏁 Script executed:

# Check if there are any other controllers with similar patterns
rg -n "split\('/'|existsSync|statSync" packages/serinus_inertia/example/ -B2 -A2

Repository: avesbox/inertia_dart

Length of output: 1549


🏁 Script executed:

# Look for any route or parameter examples in tests
fd ".*test.*" packages/serinus_inertia/example/ -type f

Repository: avesbox/inertia_dart

Length of output: 234


Validate both POSIX and Windows path separators in _containsUnsafeSegments.

The validation at line 40 only splits on /, so backslash separators can hide .. segments. On Windows-compatible Dart deployments, if URL parameters are decoded (standard HTTP behavior), a request like /assets/..%5C..%5Cfile could bypass validation. Split on both separator forms and reject empty segments.

🛡️ Recommended fix
   bool _containsUnsafeSegments(String path) {
-    return path.split('/').any((segment) => segment == '..' || segment == '.');
+    return path
+        .split(RegExp(r'[\\/]+'))
+        .any((segment) => segment.isEmpty || segment == '..' || segment == '.');
   }

Also applies to: 39-40

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/serinus_inertia/example/lib/asset_controller.dart` around lines 14 -
19, The _containsUnsafeSegments check currently only splits relativePath on '/'
so backslashes can hide traversal; update _containsUnsafeSegments to split the
incoming relativePath on both '/' and '\' (treat both as separators), reject any
empty segments, and then check each segment for '.' or '..' (or any other unsafe
token) to return true; ensure the code that reads context.params['**']
(relativePath) still uses this updated helper before constructing
File('client/dist/assets/$relativePath') so Windows-style separators cannot
bypass the validation.

Comment on lines +20 to +28
if (!assetFile.existsSync() ||
assetFile.statSync().type != FileSystemEntityType.file) {
throw NotFoundException('Asset not found');
}

context.res.contentType = ContentType.parse(
lookupMimeType(assetFile.path) ?? 'application/octet-stream',
);
context.res.contentLength = assetFile.lengthSync();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid synchronous filesystem calls in the request path.

existsSync, statSync, and lengthSync block the Dart isolate while serving static assets. Use one async stat() call and reuse its size.

⚡ Proposed async filesystem check
-    if (!assetFile.existsSync() ||
-        assetFile.statSync().type != FileSystemEntityType.file) {
+    final assetStat = await assetFile.stat();
+    if (assetStat.type != FileSystemEntityType.file) {
       throw NotFoundException('Asset not found');
     }
 
     context.res.contentType = ContentType.parse(
       lookupMimeType(assetFile.path) ?? 'application/octet-stream',
     );
-    context.res.contentLength = assetFile.lengthSync();
+    context.res.contentLength = assetStat.size;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/serinus_inertia/example/lib/asset_controller.dart` around lines 20 -
28, The code currently uses synchronous filesystem calls
(assetFile.existsSync(), assetFile.statSync(), assetFile.lengthSync()) in the
request path; replace them with a single asynchronous stat call: call await
assetFile.stat(), check the returned FileStat.type equals
FileSystemEntityType.file (throw NotFoundException('Asset not found') if not),
then reuse FileStat.size to set context.res.contentLength and keep setting
context.res.contentType as before (using lookupMimeType on assetFile.path).
Update any surrounding function to be async if needed so you can await
assetFile.stat().

Comment on lines +21 to +28
For each request:

1. Parse the request with `inertiaRequestFromHttp(request)`.
2. Build `PropertyContext` from `inertiaRequest.createContext()`.
3. Build page data with `InertiaResponseFactory().buildPageDataAsync(...)`.
4. Return JSON when `inertiaRequest.isInertia` is true.
5. Otherwise render HTML with `renderInertiaBootstrap(page)` and resolved asset tags.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Include the Inertia asset-version conflict step.

The raw flow should call out the same version check shown in RoutedInertiaMiddleware: for Inertia requests, compare the request version to the current asset version and return 409 with X-Inertia-Location before building a normal page response. Otherwise stale clients can keep using old chunks after a deploy.

📝 Suggested doc addition
 For each request:
 
 1. Parse the request with `inertiaRequestFromHttp(request)`.
 2. Build `PropertyContext` from `inertiaRequest.createContext()`.
-3. Build page data with `InertiaResponseFactory().buildPageDataAsync(...)`.
-4. Return JSON when `inertiaRequest.isInertia` is true.
-5. Otherwise render HTML with `renderInertiaBootstrap(page)` and resolved asset tags.
+3. If `inertiaRequest.isInertia` and the requested asset version is stale,
+   return `409` with the `X-Inertia-Location` header set to the current URL.
+4. Build page data with `InertiaResponseFactory().buildPageDataAsync(...)`.
+5. Return JSON when `inertiaRequest.isInertia` is true.
+6. Otherwise render HTML with `renderInertiaBootstrap(page)` and resolved asset tags.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
For each request:
1. Parse the request with `inertiaRequestFromHttp(request)`.
2. Build `PropertyContext` from `inertiaRequest.createContext()`.
3. Build page data with `InertiaResponseFactory().buildPageDataAsync(...)`.
4. Return JSON when `inertiaRequest.isInertia` is true.
5. Otherwise render HTML with `renderInertiaBootstrap(page)` and resolved asset tags.
For each request:
1. Parse the request with `inertiaRequestFromHttp(request)`.
2. Build `PropertyContext` from `inertiaRequest.createContext()`.
3. If `inertiaRequest.isInertia` and the requested asset version is stale,
return `409` with the `X-Inertia-Location` header set to the current URL.
4. Build page data with `InertiaResponseFactory().buildPageDataAsync(...)`.
5. Return JSON when `inertiaRequest.isInertia` is true.
6. Otherwise render HTML with `renderInertiaBootstrap(page)` and resolved asset tags.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/inertia-dart-frontend/references/dart-io.md` around lines 21 - 28, Add
the Inertia asset-version conflict check before building a normal page response:
when handling an Inertia request (inertiaRequest.isInertia), compare the request
version (inertiaRequest.version) to the current app asset version (the same
check logic used in RoutedInertiaMiddleware), and if they differ return an HTTP
409 with an X-Inertia-Location header pointing to the current location instead
of calling InertiaResponseFactory().buildPageDataAsync(...) or
renderInertiaBootstrap(page); ensure this early-return happens immediately after
inertiaRequestFromHttp(request) / inertiaRequest.createContext() and before any
page construction or JSON/html rendering.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the serinus_inertia example’s production story (direct Vite asset serving + SSR env wiring) and adds a portable “inertia-dart-frontend” skill with reference docs for Serinus/Routed/raw dart:io, plus deployment/Docker guidance.

Changes:

  • Add a Serinus controller to serve built Vite assets at /assets/* with immutable caching, and wire SSR options from environment variables.
  • Add production deployment documentation and container artifacts (Dockerfiles + Compose) for client-only and SSR topologies.
  • Introduce a portable inertia-dart-frontend skill with integration-specific reference guides and shared client patterns.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
skills/inertia-dart-frontend/references/serinus.md Serinus-specific integration guidance (assets, SSR, static serving options).
skills/inertia-dart-frontend/references/routed.md Routed-specific integration guidance and configuration expectations.
skills/inertia-dart-frontend/references/docker.md Reference guidance for Docker/Compose patterns and SSR topology.
skills/inertia-dart-frontend/references/deployment.md Reference guidance for production build outputs, asset serving, and SSR deployment rules.
skills/inertia-dart-frontend/references/dart-io.md Reference guidance for raw dart:io Inertia request flow, assets, and SSR.
skills/inertia-dart-frontend/references/client-patterns.md Shared frontend conventions (scaffolding, resolver, SSR hydration, asset paths).
skills/inertia-dart-frontend/agents/openai.yaml Declares the skill’s OpenAI agent metadata.
skills/inertia-dart-frontend/SKILL.md Defines the skill workflow/rules and when to load each reference.
packages/serinus_inertia/example/pubspec.yaml Adds mime dependency for correct asset content types.
packages/serinus_inertia/example/lib/asset_controller.dart Implements /assets/** serving from client/dist/assets/** with caching + HEAD support.
packages/serinus_inertia/example/lib/app_module.dart Adds SSR env-based configuration and registers the new asset controller.
packages/serinus_inertia/example/compose.yaml Adds Compose profiles for client-only and SSR stacks with renderer healthcheck gating.
packages/serinus_inertia/example/client/vite.config.js Sets Rollup input to align manifest keys with src/main.jsx entry usage.
packages/serinus_inertia/example/client/package.json Splits client vs SSR build scripts; ensures hot file is removed for production builds.
packages/serinus_inertia/example/README.md Updates docs to point to DEPLOY guide and adds Compose usage instructions.
packages/serinus_inertia/example/Dockerfile.ssr Adds multi-stage build for app + SSR renderer runtime image.
packages/serinus_inertia/example/Dockerfile.client Adds multi-stage build for the client-only runtime image.
packages/serinus_inertia/example/DEPLOY.md Adds full deployment walkthrough incl. systemd + optional nginx/Caddy patterns.
.dockerignore Excludes VCS and build artifacts from Docker build context.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


final endpoint = _envString('SERINUS_INERTIA_SSR_ENDPOINT');
if (endpoint != null) {
return InertiaSsrOptions(enabled: true, endpoint: Uri.parse(endpoint));

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SERINUS_INERTIA_SSR_ENDPOINT is parsed with Uri.parse(endpoint) without handling FormatException. If the env var is malformed, the example will crash at startup with a fairly opaque stack trace. Consider catching the parse error and either (a) disabling SSR with a clear log message, or (b) throwing a StateError/configuration exception that includes the env var name and expected format.

Suggested change
return InertiaSsrOptions(enabled: true, endpoint: Uri.parse(endpoint));
try {
return InertiaSsrOptions(enabled: true, endpoint: Uri.parse(endpoint));
} on FormatException catch (error) {
throw StateError(
'Invalid SERINUS_INERTIA_SSR_ENDPOINT value "$endpoint". '
'Expected an absolute URI such as '
'"http://127.0.0.1:13714/render". ${error.message}',
);
}

Copilot uses AI. Check for mistakes.

WORKDIR /workspace/packages/serinus_inertia/example/client
RUN npm ci
RUN npm run build

Copilot AI Apr 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This image runs npm run build, but build now includes build:ssr (SSR bundle + SSR manifest). For the client-only container profile, that extra SSR build work and output is unnecessary and increases build time/image size. Consider switching this Dockerfile to npm run build:client (and keep the SSR build only in Dockerfile.ssr).

Suggested change
RUN npm run build
RUN npm run build:client

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants