SJS follows these principles:
- Simplicity - Easy to understand, minimal abstractions
- Modularity - Each module is independent and composable
- Education - Code is written to teach, not just to work
- Zero Dependencies - Pure JavaScript, no npm packages
- Cross-Platform - Works in browsers and Node.js
- Progressive Enhancement - Use what you need, ignore the rest
┌─────────────────────────────────────────────────────────┐
│ Application Code │
│ (main.js, manifest.js) │
└───────────────────────┬─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Module Registry (SJS.modules) │
│ ┌──────────┬──────────┬──────────┬──────────┬────────┐ │
│ │ runner │ pubSub │ storage │ logger │ ... │ │
│ └──────────┴──────────┴──────────┴──────────┴────────┘ │
└───────────────────────┬─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Platform Layer │
│ ┌─────────────┬──────────────┬──────────────┬────────┐ │
│ │ Browser DOM │ Node.js FS │ IndexedDB │ Fetch │ │
│ └─────────────┴──────────────┴──────────────┴────────┘ │
└─────────────────────────────────────────────────────────┘
Each module uses an Immediately Invoked Function Expression (IIFE) to:
- Create isolated scope
- Prevent global pollution
- Control what gets exposed
(function (modules) {
// Module implementation here
const MyModule = {
method1() { },
method2() { }
};
// Register with module registry
modules.add("myModule", Object.freeze(MyModule));
})(globalThis.SJS?.modules || null);- No bundler needed - Works directly in browsers
- Clear dependencies - Explicit module registry lookup
- Immutable exports -
Object.freeze()prevents tampering - Safe failure - Null check prevents errors if core isn't loaded
Responsibility: Application initialization
┌──────────────────────────────────────────────────┐
│ 1. Load config.json │
│ 2. Load manifest.js (dependency graph) │
│ 3. Run manifest (execute tasks in order) │
│ 4. Execute main(...mainArgs) │
│ 5. Application ready │
└──────────────────────────────────────────────────┘
Key Functions:
loadJSON(path)- Loads JSON via fetch/fs/importloadModule(path)- Loads JS modules with fallbacksboot(config)- Orchestrates startup sequence
Responsibility: Module storage and retrieval
// Internal storage
const modules = {};
// Public API
{
get(id) { /* Returns module or null */ },
add(id, module) { /* Registers module */ },
list() { /* Returns all module IDs */ }
}Safety Features:
- Duplicate ID prevention
- Null checks
- Descriptive error messages
Responsibility: Dependency resolution and execution
Algorithm: Topological sort with concurrent execution
Graph: A ─┬─→ B ─→ D
└─→ C ─→ D
Execution:
1. A runs (no dependencies)
2. B and C run in parallel (both depend only on A)
3. D runs (after B and C complete)
Implementation:
// Kahn's algorithm for topological sort
while (readyNodes.length > 0 || inFlight.size > 0) {
// Launch all nodes with no pending dependencies
for (const node of readyNodes) {
inFlight.add(runNode(node));
}
// Wait for any to complete
const completed = await Promise.race(inFlight);
// Update dependency counts for nodes that depended on completed
for (const dependent of graph[completed]) {
dependencyCount[dependent]--;
if (dependencyCount[dependent] === 0) {
readyNodes.push(dependent);
}
}
}Responsibility: Event-driven communication
Pattern: Observer pattern with topic-based routing
// Internal structure
const topics = {
"user:login": [
{ cb: handler1, once: false, token: "tok-1" },
{ cb: handler2, once: true, token: "tok-2" }
]
};
// Publishing flow
publish(topic, payload) →
1. Look up topic subscribers
2. For each subscriber:
a. Call handler(payload, meta)
b. If once=true, unsubscribe
3. Return number of handlers calledFeatures:
- Wildcard topics (using patterns)
- One-time subscriptions
- Async/sync publishing
- Metadata (topic, token, timestamp)
Responsibility: Persistent key-value storage
Driver Selection:
Auto-detect environment:
↓
Browser? → IndexedDB available? → Use IndexedDB
↓ No
localStorage? → Use localStorage
↓
Node.js? → Use fs (JSON file)
↓
Fallback → Memory (not persistent)
API Surface:
{
configure(opts),
driverName(),
use(namespace), // Returns namespaced view
get(key),
set(key, value, { ttlMs }),
has(key),
remove(key),
clear(),
keys(),
entries(),
export(),
import(obj)
}TTL Implementation:
// Storage format
{
value: actualValue,
expiresAt: timestamp || null
}
// On get()
if (expiresAt && Date.now() > expiresAt) {
remove(key); // Auto-cleanup expired
return null;
}Responsibility: Structured logging with levels
Log Levels (Numeric):
DEBUG (10) - Verbose debugging
INFO (20) - Informational
WARN (30) - Warnings
ERROR (40) - Errors
OFF (99) - Disabled
Enable Patterns:
logger.enable("app:*"); // All app namespaces
logger.enable("app:auth,api"); // Specific namespaces
logger.enable("*"); // Everything
logger.disable("*"); // NothingSinks:
- Console - Standard console.log/warn/error
- Memory - Buffer for testing/debugging
- PubSub - Publish to event bus
- Custom - User-defined handlers
Responsibility: Memoization with TTL and SWR
Cache Entry Structure:
{
value: cachedValue,
createdAt: timestamp,
expiresAt: timestamp,
swrUntil: timestamp, // Stale-while-revalidate deadline
inFlight: Promise | null // Deduplication
}Memoization Flow:
Request for key
↓
In cache?
↓ Yes
Fresh? (before expiresAt)
↓ Yes → Return cached value
↓ No
Within SWR? (before swrUntil)
↓ Yes → Return stale + trigger background refresh
↓ No → Re-fetch (blocking)
↓ No → Fetch and cache
Concurrency Deduplication:
// Multiple simultaneous calls to same key
const promise = fn(); // Only ONE network call
inFlightMap.set(key, promise);
// All callers get same promise
return inFlightMap.get(key);Responsibility: Fetch wrapper with retries and caching
Request Flow:
request(opts)
↓
Run before interceptors
↓
If GET and cache enabled → Check cache
↓ Hit → Return cached
↓ Miss
↓
Retry loop (1 + retries):
↓
Create AbortController (for timeout)
↓
Call fetch()
↓ Success → Parse response → Run after interceptors → Return
↓ Network error → Retry if retryable status
↓ Timeout → Abort → Retry if attempts remain
↓
Out of retries → Throw error
Retry Strategy:
// Exponential backoff with jitter
delay = baseDelay * (backoffFactor ** attempt) + randomJitter
// Example: 300ms, 600ms±jitter, 1200ms±jitterResponsibility: Client-side routing with templates
Route Matching:
// Route patterns
"/" → Exact match
"/users/:id" → Named param (:id becomes params.id)
"/posts/:slug?" → Optional param
"/files/*" → Wildcard (matches everything after)
// Match priority
1. Exact matches
2. Param routes (by specificity)
3. Wildcard routes (last)Navigation Lifecycle:
navigate(url)
↓
Before hooks (can cancel)
↓
Match route
↓
Load data (async load() function)
↓
BeforeEnter hook (can redirect)
↓
Render template with data
↓
AfterEnter hook
↓
Update browser history
↓
Publish "router:navigate" event
Template Engine:
<!-- Evaluate: <% code %> -->
<% if (user.admin) { %>
Admin panel
<% } %>
<!-- Interpolate: <%= expr %> -->
<p>Welcome, <%= user.name %></p>
<!-- Escape: <%- expr %> -->
<p>Comment: <%- userInput %></p> <!-- HTML-escaped -->User Action
↓
Publish Event (pubSub)
↓
Business Logic (event handler)
↓
Update Storage (storage.set)
↓
Publish "data:changed" Event
↓
UI Updates (subscriber renders)
Component A needs data
↓
Check cache (cache.get)
↓ Hit → Use cached
↓ Miss
↓
Call HTTP (http.get)
↓
Store in cache (cache.set)
↓
Publish "data:loaded" event
↓
Components B, C update (subscribers)
Tab 1: storage.set("key", value)
↓
BroadcastChannel OR localStorage event
↓
Tab 2: Receives "storage:set" event
↓
Tab 2: Updates UI to reflect change
const mod = SJS.modules.get("mayNotExist");
if (!mod) {
console.warn("Optional module not loaded");
return; // Graceful degradation
}// HTTP module retries failed requests
for (let attempt = 0; attempt <= retries; attempt++) {
try {
return await fetch(url);
} catch (err) {
if (attempt === retries) throw err;
await sleep(delay * (backoffFactor ** attempt));
}
}try {
await riskyOperation();
} catch (error) {
pubSub.publish("error:occurred", { error, context });
// Centralized error handling
}- Weak references not used - Educational clarity over optimization
- LRU cache - Simple eviction in cache module (max entries)
- Cleanup on unload - Modules clean up on window/process exit
- Parallel execution - Runner executes independent tasks concurrently
- Request deduplication - Cache module prevents duplicate in-flight requests
- Lazy loading - Modules loaded only when needed
- Full bundle: ~40KB (minified, ~12KB gzipped)
- Core only: ~8KB (bootstrapper + module system)
- Modular: Import individual modules if needed
// Test module in isolation
describe("pubSub", () => {
it("should call subscribers", () => {
let called = false;
pubSub.subscribe("test", () => { called = true; });
pubSub.publish("test");
assert(called);
});
});// Test module interactions
it("should cache HTTP responses", async () => {
http.configure({ cacheGet: true });
cache.configure({ ttlMs: 5000 });
const res1 = await http.get("/data");
const res2 = await http.get("/data");
// Second request should be cached
assert(res1 === res2);
});// Test full application flow
it("should persist counter across reloads", async () => {
await storage.set("counter", 5);
// Simulate reload
location.reload();
const counter = await storage.get("counter");
assert(counter === 5);
});(function (modules) {
const MyCustomModule = {
doSomething() {
const pubSub = modules.get("pubSub");
pubSub.publish("custom:event", {});
}
};
modules.add("custom", Object.freeze(MyCustomModule));
})(globalThis.SJS?.modules);class RedisDriver {
async get(key) { /* ... */ }
async set(key, value) { /* ... */ }
// ... implement full interface
}
storage.configure({ driver: new RedisDriver() });function remoteSink(record) {
fetch("/logs", {
method: "POST",
body: JSON.stringify(record)
});
}
logger.addSink("remote", remoteSink);After studying this architecture, you should understand:
- ✅ Module patterns - IIFE, namespacing, encapsulation
- ✅ Async patterns - Promises, async/await, concurrency
- ✅ Event-driven design - Pub/sub, loose coupling
- ✅ Cross-platform abstractions - Driver patterns, feature detection
- ✅ Dependency management - Topological sort, DAG traversal
- ✅ Caching strategies - TTL, SWR, LRU eviction
- ✅ Error handling - Retry logic, graceful degradation
- ✅ Performance optimization - Deduplication, parallel execution
This architecture prioritizes learning over production optimization.
Every choice is made to teach a concept clearly.