Skip to content

Commit

Permalink
Added web-socket refreshing example to client + html pages
Browse files Browse the repository at this point in the history
  • Loading branch information
stephenwf committed Jan 10, 2025
1 parent ad7eed2 commit 29f0053
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 89 deletions.
15 changes: 10 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@
"lib"
],
"module": "lib/scripts.js",
"bin": "build/index.js",
"bin": {
"iiif-hss": "build/index.js",
"iiif-hss-dev": "build/server/entrypoint.js"
},
"author": "Stephen Fraser <[email protected]>",
"license": "MIT",
"typings": "lib/scripts.d.ts",
"scripts": {
"dev": "bun run ./src/index.ts serve",
"dev": "bun run ./src/entrypoint.ts",
"dev-cli": "bun run ./src/index.ts serve",
"build": "pnpm run build:cli && pnpm run build:server && pnpm run build:client && pnpm run build:node-client",
"build:cli": "tsup-node src/index.ts --no-splitting --outDir build --format esm --env.NODE_ENV production",
"build:server": "tsup-node src/entrypoint.ts --no-splitting --outDir build/server --format esm --env.NODE_ENV production",
Expand Down Expand Up @@ -63,14 +67,16 @@
},
"dependencies": {
"@hono/node-server": "^1.13.7",
"@hono/node-ws": "^1.0.5",
"@hono/zod-validator": "^0.4.1",
"@iiif/builder": "^2.0.1",
"@iiif/helpers": "^1.0.6",
"chalk": "^5.3.0",
"cli-progress": "^3.12.0",
"commander": "^13.0.0",
"detect-python-interpreter": "^1.0.0",
"fs-extra": "^11.1.1",
"hono": "^4.6.13",
"hono": "^4.6.16",
"js-yaml": "^4.1.0",
"keyword-extractor": "^0.0.28",
"micromatch": "^4.0.5",
Expand All @@ -85,8 +91,7 @@
"typesense": "1.7.2",
"unionfs": "^4.5.4",
"yaml": "^2.6.1",
"zod": "^3.24.1",
"commander": "^13.0.0"
"zod": "^3.24.1"
},
"devDependencies": {
"@atlas-viewer/iiif-image-api": "^2.1.1",
Expand Down
19 changes: 18 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 10 additions & 4 deletions src/commands/serve.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { serve } from "@hono/node-server";
import app from "../server";

type ServeOptions = unknown;
type ServeOptions = {
build?: boolean;
watch?: boolean;
};

export async function serveCommand(options: ServeOptions) {
console.log("Building initial assets...");

// @todo make this optional?
await app.request("/build?cache=true&emit=true&ui=true");
await app.request("/watch");
if (options.build) {
await app.request("/build?cache=true&emit=true&ui=true");
}
if (options.watch) {
await app.request("/watch");
}

console.log(`Server running: http://localhost:${app.port}`);
serve(app);
Expand Down
37 changes: 35 additions & 2 deletions src/dev/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import type { IIIFRC } from "../util/get-config.ts";
import { resolveFromSlug } from "../util/resolve-from-slug.ts";

export function create(url: string) {
export function create(
url: string,
options: {
ws?: boolean;
onFullRebuild?: () => void;
onChangeFile?: () => void;
} = {}
) {
const endpoints = {
slugs: `${url}/config/slugs.json`,
stores: `${url}/config/stores.json`,
Expand All @@ -12,7 +19,33 @@ export function create(url: string) {
overrides: `${url}/meta/overrides.json`,
};

const cache: Record<string, any> = {};
let cache: Record<string, any> = {};

if (options.ws) {
const wsUrl = new URL(url);
wsUrl.protocol = wsUrl.protocol === "https:" ? "wss:" : "ws:";
wsUrl.pathname = "/ws";
const ws = new WebSocket(wsUrl.toString());
ws.onopen = () => ws.send("ping");
ws.onmessage = (event) => {
if (event.data === "file-refresh") {
clearCache();
if (options.onChangeFile) {
options.onChangeFile();
}
}
if (event.data === "full-rebuild") {
clearCache();
if (options.onFullRebuild) {
options.onFullRebuild();
}
}
};
}

const clearCache = () => {
cache = {};
};

const cachedGet = async <T>(url: string): Promise<T> => {
if (cache[url]) {
Expand Down
34 changes: 29 additions & 5 deletions src/entrypoint.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
#!/usr/bin/env node
import { serve } from "@hono/node-server";
import app from "./server";
import { createNodeWebSocket } from "@hono/node-ws";
import server from "./server";

// @todo make this optional?
await app.request("/build?cache=true&emit=true");
const { app, emitter } = server._extra;

const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app: app });

app.get(
"/ws",
upgradeWebSocket((c) => {
return {
onOpen: (evt, ws) => {
emitter.on("*", (event, data) => {
ws.send(event);
});
},
onError: (evt, ws) => {
console.log("WebSocket error", evt);
},
};
})
);

console.log(`Server running: http://localhost:${app.port}`);
serve(app);
console.log(`Server running: http://localhost:${server.port}`);
const runningServer = serve(server);
injectWebSocket(runningServer);

// @todo make this optional?
await server.request("/build?cache=true&emit=true");
await server.request("/watch");
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ program
//
.command("serve")
.description("Serve headless static site")
.option("--no-build", "Build initial assets")
.option("--no-watch", "Watch for changes")
.action(serveCommand);

program
Expand Down
38 changes: 32 additions & 6 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { zValidator } from "@hono/zod-validator";
import { Hono } from "hono";
import { cors } from "hono/cors";
import { timeout } from "hono/timeout";
import mitt from "mitt";
import { z } from "zod";
import { type BuildOptions, build, defaultBuiltIns } from "./commands/build";
import { cloverHtml } from "./server/clover.html";
Expand All @@ -17,7 +18,13 @@ import { getConfig } from "./util/get-config";

const app = new Hono();

app.use(cors());
// app.use(cors());

const emitter = mitt<{
"file-change": { path: string };
"file-refresh": { path: string };
"full-rebuild": unknown;
}>();

// New Hono server.
//
Expand Down Expand Up @@ -99,11 +106,13 @@ app.get("/watch", async (ctx) => {
if (event.filename) {
const name = join(store.path, event.filename);
const realPath = pathCache.allPaths[name];
emitter.emit("file-change", { path: realPath });
await cachedBuild({
exact: realPath,
emit: true,
cache: true,
});
emitter.emit("file-refresh", { path: realPath });
}
}
})().catch((err) => {
Expand Down Expand Up @@ -166,7 +175,7 @@ app.get(

const { files, log, fileTypeCache, ...config } = buildConfig;

return ctx.json({
const report = {
emitted: {
stats: emitted.stats,
siteMap: emitted.siteMap,
Expand All @@ -176,7 +185,11 @@ app.get(
stores,
parsed,
config,
});
};

emitter.emit("full-rebuild", report);

return ctx.json(report);
}
);

Expand All @@ -201,7 +214,7 @@ app.post(

const { buildConfig, emitted, enrichments, extractions, parsed, stores } = await cachedBuild(body);

return ctx.json({
const report = {
emitted: {
stats: emitted.stats,
siteMap: emitted.siteMap,
Expand All @@ -211,11 +224,19 @@ app.post(
stores,
parsed,
buildConfig,
});
};

emitter.emit("full-rebuild", report);

return ctx.json(report);
}
);

app.get("/*", async (ctx) => {
app.get("/*", async (ctx, next) => {
if (ctx.req.path.startsWith("/ws")) {
await next();
return;
}
let realPath = join(cwd(), ".iiif/build", ctx.req.path);
if (realPath.endsWith("meta.json")) {
realPath = join(cwd(), ".iiif/cache", ctx.req.path);
Expand All @@ -234,6 +255,7 @@ app.get("/*", async (ctx) => {
return ctx.notFound();
});

// @ts-ignore
if (import.meta.main) {
await app.request("/build?cache=true&emit=true");
}
Expand All @@ -242,4 +264,8 @@ export default {
request: app.request,
fetch: app.fetch,
port: 7111,
_extra: {
emitter,
app,
},
};
34 changes: 24 additions & 10 deletions src/server/clover.html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,18 @@ export function cloverHtml() {
<script type="module">
import { create } from "/client.js";
let renderFn = null;
const render = () => {
if (renderFn) {
renderFn();
}
};
const $viewer = document.getElementById("viewer");
const helper = create(window.location.origin);
const helper = create(window.location.origin, {
ws: true,
onChangeFile: render,
});
if (window.location.pathname === "/clover" || window.location.pathname === "/clover/") {
$viewer.innerHTML = "No manifest selected";
Expand Down Expand Up @@ -47,15 +57,19 @@ export function cloverHtml() {
$viewer.appendChild($ul);
}
} else {
const sliced = window.location.pathname.replace("/clover", "");
const manifest = await helper.getManifest(sliced);
if (manifest) {
const clover = document.createElement("clover-viewer");
clover.setAttribute("id", manifest);
$viewer.appendChild(clover);
} else {
$viewer.innerHTML = "Manifest not found";
}
renderFn = async () => {
const sliced = window.location.pathname.replace("/clover", "");
const manifest = await helper.getManifest(sliced);
if (manifest) {
$viewer.innerHTML = "";
const clover = document.createElement("clover-viewer");
clover.setAttribute("id", manifest);
$viewer.appendChild(clover);
} else {
$viewer.innerHTML = "Manifest not found";
}
};
renderFn();
}
</script>
</body>
Expand Down
Loading

0 comments on commit 29f0053

Please sign in to comment.