Server-Side Rendering (SSR)
Basic SSR Implementation
Deno.serve with Template Engine Integration
// ssr_basic.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
import { renderToString } from "https://deno.land/x/dejs@0.10.0/mod.ts";
// Simulated data fetch
async function getPostData(id: string) {
return { id, title: `Post ${id}`, content: "This is post content" };
}
// Page template
const template = `
<!DOCTYPE html>
<html>
<head>
<title>{{ post.title }}</title>
</head>
<body>
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
</body>
</html>
`;
// SSR handler
async function handleRequest(req: Request) {
const url = new URL(req.url);
const postId = url.searchParams.get("id") || "1";
// 1. Fetch data
const post = await getPostData(postId);
// 2. Render HTML
const html = await renderToString(template, { post });
// 3. Return response
return new Response(html, {
headers: { "Content-Type": "text/html" }
});
}
// Start server
serve(handleRequest, { port: 8000 });
console.log("SSR server running on http://localhost:8000");Modern Framework Integration Example (ABC)
// ssr_abc.ts
import { Application, Router } from "https://deno.land/x/abc@v1.3.3/mod.ts";
const app = new Application();
const router = new Router();
// Simulated data store
const posts = new Map([
["1", { id: "1", title: "First Post", content: "Hello World" }],
["2", { id: "2", title: "Second Post", content: "Deno SSR" }]
]);
// SSR route
router.get("/post/:id", async (ctx) => {
const post = posts.get(ctx.params.id);
if (!post) {
ctx.response.status = 404;
return;
}
// Use ABC's built-in template engine
await ctx.render("post.ejs", { post });
});
app.use(router.routes());
app.use(router.allowedMethods());
console.log("ABC SSR server running on http://localhost:8000");
await app.listen({ port: 8000 });Data Prefetching and State Synchronization
Client-Server State Synchronization
// ssr_state_sync.ts
// Server-side code
async function handleRequest(req: Request) {
const url = new URL(req.url);
const postId = url.searchParams.get("id") || "1";
// 1. Fetch data
const post = await getPostData(postId);
// 2. Render HTML and inject initial state
const initialState = JSON.stringify({ post });
const html = `
<!DOCTYPE html>
<html>
<head>
<title>${post.title}</title>
<script>window.__INITIAL_STATE__ = ${initialState};</script>
<script src="/client.js"></script>
</head>
<body>
<div id="app">
<h1>${post.title}</h1>
<p>${post.content}</p>
</div>
</body>
</html>
`;
return new Response(html);
}
// Client-side code (client.js)
const app = {
init() {
const state = window.__INITIAL_STATE__;
if (state) {
// Use server-injected state
document.getElementById("app").innerHTML = `
<h1>${state.post.title}</h1>
<p>${state.post.content}</p>
`;
} else {
// Fetch data client-side
fetch(`/api/post?id=${window.location.search.split("=")[1]}`)
.then(res => res.json())
.then(data => {
document.getElementById("app").innerHTML = `
<h1>${data.post.title}</h1>
<p>${data.post.content}</p>
`;
});
}
}
};
app.init();SSR Performance Optimization
Caching Strategy Implementation
// ssr_caching.ts
// In-memory cache implementation
class SSRCache {
private cache = new Map();
private ttl = 60 * 1000; // 1-minute cache
async get(key: string, fallback: () => Promise<string>) {
const entry = this.cache.get(key);
if (entry && Date.now() - entry.timestamp < this.ttl) {
return entry.html;
}
const html = await fallback();
this.cache.set(key, { html, timestamp: Date.now() });
return html;
}
invalidate(key: string) {
this.cache.delete(key);
}
}
// SSR handler with caching
const cache = new SSRCache();
async function handleRequest(req: Request) {
const url = new URL(req.url);
const postId = url.searchParams.get("id") || "1";
return new Response(
await cache.get(`post:${postId}`, async () => {
const post = await getPostData(postId);
return renderToString(template, { post });
}),
{ headers: { "Content-Type": "text/html" } }
);
}Streaming Rendering Optimization
// ssr_streaming.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
async function handleRequest(req: Request) {
const { writable, readable } = new TransformStream();
const writer = writable.getWriter();
// Immediately send HTML header
await writer.write(new TextEncoder().encode(`
<!DOCTYPE html>
<html>
<head>
<title>Streaming SSR</title>
</head>
<body>
`));
// Simulate async data fetch
const postPromise = getPostData("1");
// Send immediately displayable content
await writer.write(new TextEncoder().encode(`
<div>Loading...</div>
`));
// Send remaining content after data fetch
const post = await postPromise;
await writer.write(new TextEncoder().encode(`
<h1>${post.title}</h1>
<p>${post.content}</p>
`));
// Close HTML document
await writer.write(new TextEncoder().encode(`
</body>
</html>
`));
await writer.close();
return new Response(readable, {
headers: { "Content-Type": "text/html" }
});
}
serve(handleRequest, { port: 8000 });SSR Security and Error Handling
Security Protection Measures
// ssr_security.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
// XSS protection function
function escapeHtml(unsafe: string): string {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// Error handling middleware
async function errorHandler(fn: (req: Request) => Promise<Response>) {
return async (req: Request) => {
try {
return await fn(req);
} catch (err) {
console.error("SSR Error:", err);
return new Response(
"500 Internal Server Error",
{ status: 500 }
);
}
};
}
// Secure SSR handler
const safeHandler = errorHandler(async (req: Request) => {
const url = new URL(req.url);
const postId = url.searchParams.get("id") || "1";
// Input validation
if (!/^\d+$/.test(postId)) {
return new Response("Invalid post ID", { status: 400 });
}
const post = await getPostData(postId);
// Secure rendering
const html = `
<!DOCTYPE html>
<html>
<head>
<title>${escapeHtml(post.title)}</title>
</head>
<body>
<h1>${escapeHtml(post.title)}</h1>
<p>${escapeHtml(post.content)}</p>
</body>
</html>
`;
return new Response(html);
});
serve(safeHandler, { port: 8000 });SSR Application Scenarios
SEO Optimization Implementation
// ssr_seo.ts
async function handleRequest(req: Request) {
const url = new URL(req.url);
const postId = url.searchParams.get("id") || "1";
// Fetch complete page data
const post = await getPostData(postId);
const relatedPosts = await getRelatedPosts(postId);
// Render complete HTML (including all SEO elements)
const html = `
<!DOCTYPE html>
<html>
<head>
<title>${post.title}</title>
<meta name="description" content="${post.content.slice(0, 160)}">
<meta name="keywords" content="blog, post, ${post.title}">
<meta name="robots" content="index, follow">
</head>
<body>
<article>
<h1>${post.title}</h1>
<p>${post.content}</p>
</article>
<section>
<h2>Related Posts</h2>
<ul>
${relatedPosts.map(p => `
<li><a href="/post?id=${p.id}">${p.title}</a></li>
`).join("")}
</ul>
</section>
</body>
</html>
`;
return new Response(html, {
headers: {
"Content-Type": "text/html",
"X-Robots-Tag": "index, follow" // SEO directive
}
});
}First-Screen Performance Optimization
// ssr_performance.ts
async function handleRequest(req: Request) {
const url = new URL(req.url);
const postId = url.searchParams.get("id") || "1";
// 1. Start response immediately
const { writable, readable } = new TransformStream();
const writer = writable.getWriter();
// 2. Send critical CSS and HTML skeleton first
await writer.write(new TextEncoder().encode(`
<!DOCTYPE html>
<html>
<head>
<title>Loading...</title>
<style>
body { font-family: Arial; }
.loading { text-align: center; }
</style>
</head>
<body>
<div id="app" class="loading">Loading...</div>
`));
// 3. Fetch data and render remaining content in parallel
const postPromise = getPostData(postId);
// 4. Send immediately displayable content
await writer.write(new TextEncoder().encode(`
<p>Loading content...</p>
`));
// 5. Update DOM after data fetch
const post = await postPromise;
await writer.write(new TextEncoder().encode(`
<script>
document.getElementById("app").innerHTML = \`
<h1>${post.title}</h1>
<p>${post.content}</p>
\`;
</script>
`));
// 6. Close document
await writer.write(new TextEncoder().encode(`
</body>
</html>
`));
await writer.close();
return new Response(readable, {
headers: { "Content-Type": "text/html" }
});
}Static Site Generation (SSG)
Basic SSG Implementation
Deno.build with File Generation
// ssg_basic.ts
import { ensureDir, writeTextFile } from "https://deno.land/std@0.224.0/fs/mod.ts";
// Simulated content data
const posts = [
{ id: "1", title: "First Post", content: "Hello World" },
{ id: "2", title: "Second Post", content: "Deno SSG" }
];
// Template function
function renderPostTemplate(post: typeof posts[0]) {
return `
<!DOCTYPE html>
<html>
<head>
<title>${post.title}</title>
</head>
<body>
<h1>${post.title}</h1>
<p>${post.content}</p>
</body>
</html>
`;
}
// Generate static files
async function generateStaticSite() {
// Ensure output directory exists
await ensureDir("./dist/posts");
// Generate post pages
for (const post of posts) {
const content = renderPostTemplate(post);
await writeTextFile(`./dist/posts/${post.id}.html`, content);
}
// Generate index page
const indexContent = `
<!DOCTYPE html>
<html>
<head>
<title>Blog Posts</title>
</head>
<body>
<h1>Blog Posts</h1>
<ul>
${posts.map(p => `
<li><a href="/posts/${p.id}.html">${p.title}</a></li>
`).join("")}
</ul>
</body>
</html>
`;
await writeTextFile("./dist/index.html", indexContent);
console.log("Static site generated in ./dist");
}
generateStaticSite();Content Management and Route Generation
Dynamic Route Generator
// ssg_routing.ts
interface ContentItem {
id: string;
title: string;
content: string;
tags?: string[];
}
// Simulated content data
const contents: ContentItem[] = [
{ id: "1", title: "Deno Introduction", content: "Deno is a modern runtime" },
{ id: "2", title: "SSG Guide", content: "Static Site Generation guide" },
{ id: "3", title: "Web Development", content: "Modern web dev techniques", tags: ["web", "dev"] }
];
// Generate route configuration
function generateRoutes(contents: ContentItem[]) {
// 1. Generate post routes
const postRoutes = contents.map(item => ({
path: `/posts/${item.id}`,
template: "post",
data: item
}));
// 2. Generate tag routes
const tags = new Set<string>();
contents.forEach(item => {
if (item.tags) {
item.tags.forEach(tag => tags.add(tag));
}
});
const tagRoutes = Array.from(tags).map(tag => ({
path: `/tags/${tag}`,
template: "tag",
data: { tag, items: contents.filter(item => item.tags?.includes(tag)) }
}));
// 3. Generate index route
const indexRoute = {
path: "/",
template: "index",
data: { items: contents }
};
return [indexRoute, ...postRoutes, ...tagRoutes];
}
// Generate static site
async function generateSite() {
const routes = generateRoutes(contents);
await ensureDir("./dist");
for (const route of routes) {
let content = "";
switch (route.template) {
case "post":
content = `
<!DOCTYPE html>
<html>
<head>
<title>${route.data.title}</title>
</head>
<body>
<h1>${route.data.title}</h1>
<p>${route.data.content}</p>
</body>
</html>
`;
break;
case "tag":
content = `
<!DOCTYPE html>
<html>
<head>
<title>Posts tagged with ${route.data.tag}</title>
</head>
<body>
<h1>Posts tagged with ${route.data.tag}</h1>
<ul>
${route.data.items.map(item => `
<li><a href="/posts/${item.id}.html">${item.title}</a></li>
`).join("")}
</ul>
</body>
</html>
`;
break;
case "index":
content = `
<!DOCTYPE html>
<html>
<head>
<title>Blog Posts</title>
</head>
<body>
<h1>Blog Posts</h1>
<ul>
${route.data.items.map(item => `
<li><a href="/posts/${item.id}.html">${item.title}</a></li>
`).join("")}
</ul>
</body>
</html>
`;
break;
}
const outputPath = route.path === "/"
? "./dist/index.html"
: `./dist${route.path}.html`;
await ensureDir(new URL(outputPath, import.meta.url).pathname.split("/").slice(0, -1).join("/"));
await writeTextFile(outputPath, content);
}
console.log("Static site with dynamic routes generated");
}
generateSite();SSG Performance Optimization
Incremental Build Implementation
// ssg_incremental.ts
import { walk } from "https://deno.land/std@0.224.0/fs/walk.ts";
// Track file modification times
const fileTimestamps = new Map<string, number>();
// Initialize file timestamps
async function initFileTimestamps(dir: string) {
for await (const entry of walk(dir)) {
if (entry.isFile) {
fileTimestamps.set(entry.path, entry.mtime?.getTime() || 0);
}
}
}
// Check if file needs rebuilding
function needsRebuild(filePath: string): boolean {
const currentMtime = Deno.statSync(filePath).mtime?.getTime() || 0;
const lastMtime = fileTimestamps.get(filePath) || 0;
return currentMtime > lastMtime;
}
// Update file timestamp
function updateTimestamp(filePath: string) {
fileTimestamps.set(filePath, Deno.statSync(filePath).mtime?.getTime() || 0);
}
// Incremental site build
async function incrementalBuild(contentDir: string, outputDir: string) {
await initFileTimestamps(contentDir);
// 1. Check for content file changes
const contentFiles = Array.from(fileTimestamps.keys())
.filter(path => path.startsWith(contentDir));
const changedFiles = contentFiles.filter(needsRebuild);
if (changedFiles.length === 0) {
console.log("No changes detected, skipping build");
return;
}
console.log(`Rebuilding ${changedFiles.length} changed files`);
// 2. Rebuild affected pages
// (Simplified; actual logic should determine affected pages based on content changes)
await generateSite(); // Implement finer-grained update logic in practice
// 3. Update all file timestamps
for (const path of fileTimestamps.keys()) {
updateTimestamp(path);
}
}
// Usage example
incrementalBuild("./content", "./dist");SSG and CMS Integration
Headless CMS Integration Example
// ssg_cms.ts
interface CmsPost {
id: string;
title: string;
content: string;
publishedAt: string;
}
// Fetch content from CMS
async function fetchCmsContent(): Promise<CmsPost[]> {
// Should use actual CMS API
const response = await fetch("https://cms.example.com/api/posts");
if (!response.ok) {
throw new Error("Failed to fetch CMS content");
}
return response.json();
}
// Generate static site
async function generateSiteFromCms() {
try {
const posts = await fetchCmsContent();
// Generate post pages
for (const post of posts) {
const content = `
<!DOCTYPE html>
<html>
<head>
<title>${post.title}</title>
</head>
<body>
<h1>${post.title}</h1>
<p>${post.content}</p>
<p>Published: ${new Date(post.publishedAt).toLocaleDateString()}</p>
</body>
</html>
`;
await writeTextFile(`./dist/posts/${post.id}.html`, content);
}
// Generate index page
const indexContent = `
<!DOCTYPE html>
<html>
<head>
<title>Blog Posts</title>
</head>
<body>
<h1>Blog Posts</h1>
<ul>
${posts.map(p => `
<li><a href="/posts/${p.id}.html">${p.title}</a></li>
`).join("")}
</ul>
</body>
</html>
`;
await writeTextFile("./dist/index.html", indexContent);
console.log("Static site generated from CMS content");
} catch (err) {
console.error("Failed to generate site:", err);
}
}
generateSiteFromCms();SSG Application Scenarios
Documentation Site Generation
// ssg_docs.ts
interface DocPage {
id: string;
title: string;
content: string;
navOrder: number;
}
// Documentation data
const docs: DocPage[] = [
{ id: "getting-started", title: "Getting Started", content: "# Getting Started\n\nInstall Deno...", navOrder: 1 },
{ id: "ssg-guide", title: "SSG Guide", content: "# SSG Guide\n\nStatic Site Generation with Deno...", navOrder: 2 },
{ id: "api-reference", title: "API Reference", content: "# API Reference\n\nDetailed API documentation...", navOrder: 3 }
];
// Generate documentation site
async function generateDocsSite() {
// 1. Generate documentation pages
for (const doc of docs.sort((a, b) => a.navOrder - b.navOrder)) {
const content = `
<!DOCTYPE html>
<html>
<head>
<title>${doc.title}</title>
</head>
<body>
<nav>
<ul>
${docs.sort((a, b) => a.navOrder - b.navOrder)
.map(d => `
<li><a href="/docs/${d.id}.html">${d.title}</a></li>
`).join("")}
</ul>
</nav>
<main>
${doc.content}
</main>
</body>
</html>
`;
await writeTextFile(`./dist/docs/${doc.id}.html`, content);
}
// 2. Generate index page
const indexContent = `
<!DOCTYPE html>
<html>
<head>
<title>Documentation</title>
</head>
<body>
<h1>Documentation</h1>
<ul>
${docs.sort((a, b) => a.navOrder - b.navOrder)
.map(d => `
<li><a href="/docs/${d.id}.html">${d.title}</a></li>
`).join("")}
</ul>
</body>
</html>
`;
await writeTextFile("./dist/index.html", indexContent);
// 3. Copy static assets
await ensureDir("./dist/styles");
// Should copy CSS/JS assets in practice
console.log("Documentation site generated");
}
generateDocsSite();Real-Time Application Development
WebSocket Real-Time Communication
Basic WebSocket Implementation
// websocket_basic.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
const server = serve({ port: 8000 });
console.log("WebSocket server running on ws://localhost:8000");
// Store all connected clients
const clients = new Set<WebSocket>();
for await (const req of server) {
// Upgrade to WebSocket connection
const { socket, response } = Deno.upgradeWebSocket(req);
// Add to client set
clients.add(socket);
console.log(`New client connected. Total: ${clients.size}`);
// Message handling
socket.onmessage = (e) => {
console.log("Received message:", e.data);
// Broadcast message to all clients
for (const client of clients) {
if (client !== socket && client.readyState === WebSocket.OPEN) {
client.send(e.data);
}
}
};
// Connection close handling
socket.onclose = () => {
clients.delete(socket);
console.log(`Client disconnected. Total: ${clients.size}`);
};
// Error handling
socket.onerror = (e) => {
console.error("WebSocket error:", e);
clients.delete(socket);
};
}Room/Channel Implementation
// websocket_rooms.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
// Room manager
class RoomManager {
private rooms: Map<string, Set<WebSocket>> = new Map();
// Join room
joinRoom(roomId: string, socket: WebSocket) {
if (!this.rooms.has(roomId)) {
this.rooms.set(roomId, new Set());
}
this.rooms.get(roomId)?.add(socket);
console.log(`Client joined room ${roomId}. Total: ${this.rooms.get(roomId)?.size || 0}`);
}
// Leave room
leaveRoom(roomId: string, socket: WebSocket) {
this.rooms.get(roomId)?.delete(socket);
console.log(`Client left room ${roomId}. Total: ${this.rooms.get(roomId)?.size || 0}`);
}
// Broadcast message to room
broadcast(roomId: string, message: string) {
this.rooms.get(roomId)?.forEach(socket => {
if (socket.readyState === WebSocket.OPEN) {
socket.send(message);
}
});
}
}
const roomManager = new RoomManager();
const server = serve({ port: 8000 });
console.log("WebSocket room server running on ws://localhost:8000");
for await (const req of server) {
const { socket, response } = Deno.upgradeWebSocket(req);
// Client sends join room message
socket.onmessage = (e) => {
try {
const data = JSON.parse(e.data);
if (data.type === "join") {
roomManager.joinRoom(data.roomId, socket);
} else if (data.type === "message") {
roomManager.broadcast(data.roomId, e.data);
}
} catch (err) {
console.error("Message parse error:", err);
}
};
socket.onclose = () => {
// Should track rooms for each socket in practice
// Simplified handling here
console.log("Client disconnected");
};
}GraphQL and Deno Integration
Apollo Server Implementation
// graphql_apollo.ts
import { Application, Router } from "https://deno.land/x/abc@v1.3.3/mod.ts";
import { ApolloServer } from "https://deno.land/x/apollo@0.8.0/mod.ts";
import { gql } from "https://deno.land/x/graphql_tag@0.1.2/mod.ts";
// Define GraphQL Schema
const typeDefs = gql`
type Post {
id: ID!
title: String!
content: String!
}
type Query {
posts: [Post!]!
post(id: ID!): Post
}
type Mutation {
createPost(title: String!, content: String!): Post!
}
`;
// Simulated data store
const posts = new Map<string, { id: string; title: string; content: string }>();
// Resolver implementation
const resolvers = {
Query: {
posts: () => Array.from(posts.values()),
post: (_: any, args: { id: string }) => posts.get(args.id),
},
Mutation: {
createPost: (_: any, args: { title: string; content: string }) => {
const id = Date.now().toString();
const post = { id, title: args.title, content: args.content };
posts.set(id, post);
return post;
},
},
};
// Create Apollo Server
const server = new ApolloServer({
typeDefs,
resolvers,
});
// Integrate with ABC framework
const app = new Application();
const router = new Router();
server.applyMiddleware({ app });
router.get("/", (ctx) => {
ctx.response.body = "GraphQL Server running";
});
app.use(router.routes());
app.use(router.allowedMethods());
console.log("GraphQL server running on http://localhost:8000/graphql");
await app.listen({ port: 8000 });Subscription Functionality Implementation
// graphql_subscriptions.ts
import { Application, Router } from "https://deno.land/x/abc@v1.3.3/mod.ts";
import { ApolloServer } from "https://deno.land/x/apollo@0.8.0/mod.ts";
import { gql } from "https://deno.land/x/graphql_tag@0.1.2/mod.ts";
import { PubSub } from "https://deno.land/x/apollo@0.8.0/deps.ts";
// PubSub implementation
const pubsub = new PubSub();
// Define Schema with subscriptions
const typeDefs = gql`
type Post {
id: ID!
title: String!
content: String!
}
type Query {
posts: [Post!]!
}
type Mutation {
createPost(title: String!, content: String!): Post!
}
type Subscription {
postCreated: Post!
}
`;
// Resolver implementation
const resolvers = {
Query: {
posts: () => Array.from(posts.values()),
},
Mutation: {
createPost: (_: any, args: { title: string; content: string }) => {
const id = Date.now().toString();
const post = { id, title: args.title, content: args.content };
posts.set(id, post);
// Publish event
pubsub.publish("POST_CREATED", { postCreated: post });
return post;
},
},
Subscription: {
postCreated: {
subscribe: () => pubsub.asyncIterator(["POST_CREATED"]),
},
},
};
// Create Apollo Server (with subscriptions)
const server = new ApolloServer({
typeDefs,
resolvers,
subscriptions: {
path: "/subscriptions",
},
});
// Integrate with ABC framework
const app = new Application();
const router = new Router();
server.applyMiddleware({ app });
router.get("/", (ctx) => {
ctx.response.body = "GraphQL Server with Subscriptions running";
});
app.use(router.routes());
app.use(router.allowedMethods());
console.log("GraphQL server with subscriptions running");
console.log("HTTP endpoint: http://localhost:8000/graphql");
console.log("WS endpoint: ws://localhost:8000/subscriptions");
await app.listen({ port: 8000 });Real-Time Collaborative Editing
Simplified CRDT Algorithm Implementation
// crdt_basic.ts
// Simplified CRDT character list implementation
class CRDTDocument {
private characters: Array<{ id: string; value: string; siteId: string; counter: number }> = [];
private siteId: string;
private counter: number = 0;
constructor(siteId: string) {
this.siteId = siteId;
}
// Local insert
localInsert(position: number, value: string): void {
this.counter++;
const charId = `${this.siteId}-${this.counter}`;
this.characters.splice(position, 0, {
id: charId,
value,
siteId: this.siteId,
counter: this.counter,
});
}
// Remote insert
remoteInsert(char: { id: string; value: string; siteId: string; counter: number }): void {
// Find insertion position
let insertAt = this.characters.findIndex(
(c) => c.counter > char.counter || (c.counter === char.counter && c.siteId > char.siteId)
);
if (insertAt === -1) {
insertAt = this.characters.length;
}
this.characters.splice(insertAt, 0, char);
}
// Local delete
localDelete(position: number): void {
this.characters.splice(position, 1);
}
// Remote delete (by ID)
remoteDelete(charId: string): void {
const index = this.characters.findIndex((c) => c.id === charId);
if (index !== -1) {
this.characters.splice(index, 1);
}
}
// Get document content
getContent(): string {
return this.characters.map((c) => c.value).join("");
}
}
// Usage example
const doc1 = new CRDTDocument("site1");
const doc2 = new CRDTDocument("site2");
// Site 1 local inserts
doc1.localInsert(0, "H");
doc1.localInsert(1, "e");
doc1.localInsert(2, "l");
doc1.localInsert(3, "l");
doc1.localInsert(4, "o");
// Site 2 local inserts (concurrent editing)
doc2.localInsert(0, "H");
doc2.localInsert(1, "i"); // Different content
doc2.localInsert(2, "l");
doc2.localInsert(3, "l");
doc2.localInsert(4, "o");
// Sync operations (simulate network transfer)
// Site 1 receives Site 2's operations
doc1.remoteInsert({ id: "site2-1", value: "H", siteId: "site2", counter: 1 });
doc1.remoteInsert({ id: "site2-2", value: "i", siteId: "site2", counter: 2 });
doc1.remoteInsert({ id: "site2-3", value: "l", siteId: "site2", counter: 3 });
doc1.remoteInsert({ id: "site2-4", value: "l", siteId: "site2", counter: 4 });
doc1.remoteInsert({ id: "site2-5", value: "o", siteId: "site2", counter: 5 });
// Final content (merged result)
console.log("Merged content:", doc1.getContent()); // Should include all charactersComplete Collaborative Editor Implementation
// collab_editor.ts
// Simplified real-time collaborative editor
class CollaborativeEditor {
private document = new CRDTDocument(Deno.env.get("SITE_ID") || "local");
private clients = new Set<WebSocket>();
private wsServer: Deno.HttpServer;
constructor(port: number) {
this.wsServer = serve({ port });
this.setupWebSocketServer();
}
private setupWebSocketServer() {
for await (const req of this.wsServer) {
const { socket, response } = Deno.upgradeWebSocket(req);
this.clients.add(socket);
// Send current document state to new client
socket.onopen = () => {
socket.send(JSON.stringify({
type: "init",
content: this.document.getContent(),
}));
};
// Handle client operations
socket.onmessage = (e) => {
try {
const message = JSON.parse(e.data);
if (message.type === "insert") {
this.document.localInsert(message.position, message.value);
this.broadcast({
type: "insert",
position: message.position,
value: message.value,
charId: message.charId,
});
} else if (message.type === "delete") {
this.document.localDelete(message.position);
this.broadcast({
type: "delete",
position: message.position,
charId: message.charId,
});
}
} catch (err) {
console.error("Message error:", err);
}
};
// Forward remote operations
const forwardMessage = (message: any) => {
if (socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify(message));
}
};
// Receive and forward remote operations to other clients
socket.onmessage = (e) => {
const message = JSON.parse(e.data);
if (message.type === "insert") {
this.document.remoteInsert({
id: message.charId,
value: message.value,
siteId: message.siteId,
counter: message.counter,
});
this.clients.forEach((client) => {
if (client !== socket) forwardMessage(message);
});
} else if (message.type === "delete") {
this.document.remoteDelete(message.charId);
this.clients.forEach((client) => {
if (client !== socket) forwardMessage(message);
});
}
};
// Connection close handling
socket.onclose = () => {
this.clients.delete(socket);
};
}
}
// Broadcast message to all clients
private broadcast(message: any) {
this.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(message));
}
});
}
}
// Usage example
const editor = new CollaborativeEditor(8080);
console.log("Collaborative editor server running on ws://localhost:8080");
// Simulate client operation (should be initiated by frontend in practice)
setTimeout(() => {
// Simulate insert operation
const charId = `server-${Date.now()}`;
editor["document"].localInsert(0, "H");
editor.broadcast({
type: "insert",
position: 0,
value: "H",
charId,
siteId: "server",
counter: 1,
});
}, 1000);Real-Time Communication Performance Optimization
WebSocket Message Compression
// websocket_compression.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
import { compress, decompress } from "https://deno.land/std@0.224.0/encoding/gzip.ts";
const server = serve({ port: 8000 });
console.log("Compressed WebSocket server running on ws://localhost:8000");
// Store client connections and compression state
const clients = new Map<WebSocket, { compressor: TextEncoder; decompressor: TextDecoder }>();
for await (const req of server) {
const { socket, response } = Deno.upgradeWebSocket(req);
// Initialize compressor
const compressor = new TextEncoder();
const decompressor = new TextDecoder();
clients.set(socket, { compressor, decompressor });
// Message receive handling (with decompression)
socket.onmessage = async (e) => {
try {
// Should use more efficient compression (e.g., pako) in practice
const decompressed = decompressor.decode(await decompress(e.data));
console.log("Received:", decompressed);
// Process message...
const response = `Echo: ${decompressed}`;
// Send compressed response (simplified example)
const compressed = await compress(compressor.encode(response));
socket.send(compressed);
} catch (err) {
console.error("Message error:", err);
}
};
// Connection close handling
socket.onclose = () => {
clients.delete(socket);
};
}Batch Message Processing
// websocket_batching.ts
class MessageBatcher {
private batch: any[] = [];
private batchSize: number;
private flushInterval: number;
private timer: number | null = null;
constructor(batchSize: number = 10, flushInterval: number = 100) {
this.batchSize = batchSize;
this.flushInterval = flushInterval;
this.startFlushTimer();
}
addMessage(message: any) {
this.batch.push(message);
if (this.batch.length >= this.batchSize) {
this.flush();
}
}
private startFlushTimer() {
this.timer = setInterval(() => {
if (this.batch.length > 0) {
this.flush();
}
}, this.flushInterval) as unknown as number;
}
private flush() {
if (this.batch.length === 0) return;
// Send batch messages
const batchToSend = [...this.batch];
this.batch = [];
// Should send via WebSocket in practice
console.log("Flushing batch:", batchToSend);
// socket.send(JSON.stringify(batchToSend));
}
destroy() {
if (this.timer) {
clearInterval(this.timer);
}
}
}
// Usage example
const batcher = new MessageBatcher(5, 50); // Flush every 5 messages or 50ms
// Simulate message generation
setInterval(() => {
batcher.addMessage({ type: "update", data: Date.now() });
}, 10);Real-Time Application Security and Permission Control
WebSocket Authentication and Authorization
// websocket_auth.ts
import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
// Simulated user database
const users = new Map<string, { token: string; permissions: string[] }>([
["user1", { token: "token1", permissions: ["read", "write"] }],
["user2", { token: "token2", permissions: ["read"] }],
]);
const server = serve({ port: 8000 });
console.log("Authenticated WebSocket server running on ws://localhost:8000");
for await (const req of server) {
// 1. Get token from query parameters
const url = new URL(req.url);
const token = url.searchParams.get("token");
// 2. Validate token
let user;
for (const [_, u] of users) {
if (u.token === token) {
user = u;
break;
}
}
if (!token || !user) {
req.respond({ status: 401, body: "Unauthorized" });
continue;
}
// 3. Upgrade to WebSocket connection
const { socket, response } = Deno.upgradeWebSocket(req);
// 4. Permission check example
socket.onmessage = (e) => {
try {
const message = JSON.parse(e.data);
// Check write permission
if (message.type === "write" && !user.permissions.includes("write")) {
socket.send(JSON.stringify({
type: "error",
message: "Permission denied"
}));
return;
}
// Process message...
console.log("Authorized message:", message);
socket.send(JSON.stringify({ type: "ack" }));
} catch (err) {
console.error("Message error:", err);
}
};
socket.onclose = () => {
console.log(`User ${token} disconnected`);
};
}Rate Limiting Implementation
// websocket_rate_limit.ts
class RateLimiter {
private requests: Map<string, { count: number; timestamp: number }> = new Map();
private windowSize: number; // Milliseconds
private maxRequests: number;
constructor(windowSize: number = 1000, maxRequests: number = 10) {
this.windowSize = windowSize;
this.maxRequests = maxRequests;
}
checkLimit(key: string): boolean {
const now = Date.now();
const record = this.requests.get(key);
if (!record) {
this.requests.set(key, { count: 1, timestamp: now });
return true;
}
if (now - record.timestamp > this.windowSize) {
// New time window
this.requests.set(key, { count: 1, timestamp: now });
return true;
}
if (record.count >= this.maxRequests) {
return false; // Exceeds limit
}
// Increment count
this.requests.set(key, { count: record.count + 1, timestamp: record.timestamp });
return true;
}
}
// Usage example
const limiter = new RateLimiter(1000, 5); // Max 5 messages per second
const server = serve({ port: 8000 });
console.log("Rate limited WebSocket server running on ws://localhost:8000");
for await (const req of server) {
const { socket, response } = Deno.upgradeWebSocket(req);
socket.onmessage = (e) => {
const clientIp = req.conn.remoteAddr.hostname; // Simplified; use more reliable client ID in practice
if (!limiter.checkLimit(clientIp)) {
socket.send(JSON.stringify({
type: "error",
message: "Too many requests"
}));
return;
}
// Process message...
console.log("Processing message:", e.data);
socket.send(JSON.stringify({ type: "ack" }));
};
}



