diff --git a/Island.cfm b/Island.cfm
index 93c3216..c640890 100644
--- a/Island.cfm
+++ b/Island.cfm
@@ -49,7 +49,10 @@ function resolveAsset(required string path) {
if (cfg.isDev) {
// Strip leading "./" so we get a clean URL join
var clean = reReplace(arguments.path, "^\./", "");
- return "http://localhost:#cfg.vitePort#/#clean#";
+ // viteUrl override (from COLDSPA_VITE_URL env) lets the browser reach
+ // a Vite dev server that isn't on localhost (e.g. host.docker.internal).
+ var viteBase = cfg.viteUrl ?: ("http://localhost:" & cfg.vitePort);
+ return viteBase & "/" & clean;
}
// Production: look up content-hashed file in vite manifest
@@ -97,10 +100,37 @@ if (isSimpleValue(rendered)) {
}
bootImports = rendered.imports;
bootBody = rendered.body;
+
+// Server-side rendering. If the renderer supports ssrRender(), call the SSR
+// sidecar and embed the returned HTML inside the mount div. The client's
+// createSSRApp().mount() will hydrate it on load. If SSR fails or isn't
+// available, we just emit an empty div and the client mounts fresh -- so the
+// page works either way (just no JS-off rendering in that case).
+ssrHtml = "";
+ssrError = "";
+if (structKeyExists(attributes.framework, "ssrRender")) {
+ ssrResult = attributes.framework.ssrRender(componentGlobKey, attributes.props);
+ // Tolerate older renderers that returned a plain string.
+ if (isStruct(ssrResult)) {
+ ssrHtml = ssrResult.html ?: "";
+ ssrError = ssrResult.error ?: "";
+ } else {
+ ssrHtml = ssrResult;
+ }
+}
-
-
+
+
+
+
+
+
+
+
+
+
+#ssrHtml#
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a5ba989
--- /dev/null
+++ b/README.md
@@ -0,0 +1,99 @@
+# Coldspa
+
+Astro-style framework islands for ColdFusion. Mount Vue (and React, soon) components into CFML pages with server-side rendering and progressive hydration.
+
+```cfml
+
+
+
+
+```
+
+## Quick start
+
+```bash
+npm install
+npm run dev # Vite dev server (HMR)
+npm run ssr # Node SSR sidecar
+```
+
+For production:
+
+```bash
+npm run build # builds client + SSR bundles
+npm run ssr:prod # runs the sidecar against the built bundle
+```
+
+## Configuration
+
+Coldspa resolves config in this order (highest priority first):
+
+1. **Environment variables** — for CI/CD, Docker, prod
+2. **`island-config.json`** in the webroot — for local dev / Admin UI
+3. **Built-in defaults**
+
+### Environment variables
+
+| Variable | Effect |
+|---------------------|--------------------------------------------------------------------------|
+| `CF_ENV` | `development` / `dev` switches Coldspa to dev mode |
+| `COLDSPA_SSR_URL` | Where **CF** reaches the SSR sidecar (server-to-server) |
+| `COLDSPA_VITE_URL` | Where the **browser** reaches the Vite dev server (used for asset URLs) |
+| `COLDSPA_SSR_PORT` | (sidecar) Port to listen on. Default `5174` |
+| `COLDSPA_SSR_HOST` | (sidecar) Bind address. Default `0.0.0.0`. Use `127.0.0.1` to lock down |
+| `NODE_ENV` | (sidecar) `production` switches sidecar to use built bundle |
+
+### `island-config.json`
+
+Lives in the webroot. Should be `.gitignore`d (it can be edited via the CF Admin UI). Any keys you set here are merged on top of defaults but overridden by env vars.
+
+```json
+{
+ "isDev": true,
+ "ssrUrl": "http://127.0.0.1:5174",
+ "viteUrl": "http://localhost:5173",
+ "vitePort": "5173"
+}
+```
+
+| Key | Default | Description |
+|------------|--------------------------|-------------------------------------------------------------|
+| `isDev` | `false` | Dev mode (uses Vite dev server) vs prod (uses `dist/`) |
+| `ssrUrl` | `http://127.0.0.1:5174` | SSR sidecar URL (server-to-server) |
+| `viteUrl` | unset | Browser-facing Vite URL. Falls back to `localhost:vitePort` |
+| `vitePort` | `"5173"` | Used to build the default `viteUrl` if `viteUrl` is unset |
+
+After editing `island-config.json`, request any page with `?reloadApp=1` to bust the cached config (or restart CF).
+
+### Docker / cross-host setups
+
+Two URLs need to work from **different perspectives**:
+
+- `ssrUrl` — CF → Node sidecar (server-to-server). Set this on the CF container.
+- `viteUrl` — Browser → Vite dev server. Set this so generated `